Skip to content

Commit

Permalink
Merge pull request #235 from google/moe_writing_branch_from_061401e8f…
Browse files Browse the repository at this point in the history
…8d2b201f88f2bfd7509a49b2433685c

Merge from moe writing branch from 061401e
  • Loading branch information
eamonnmcmanus committed May 6, 2015
2 parents 5839c28 + acc0ea3 commit e72ded3
Show file tree
Hide file tree
Showing 17 changed files with 584 additions and 136 deletions.
60 changes: 57 additions & 3 deletions common/README.md
Expand Up @@ -3,18 +3,26 @@ Auto Common Utilities

## Overview

The Auto project has a set of common utilities to help ease use of the annotation processing environment.
The Auto project has a set of common utilities to help ease use of the annotation processing
environment.

## Utility classes of note

* MoreTypes - utilities and Equivalence wrappers for TypeMirror and related subtypes
* MoreElements - utilities for Element and related subtypes
* SuperficialValidation - very simple scanner to ensure an Element is valid and free from distortion from upstream compilation errors
* SuperficialValidation - very simple scanner to ensure an Element is valid and free from
distortion from upstream compilation errors
* Visibility - utilities for working with Elements' visibility levels (public, protected, etc.)
* BasicAnnotationProcessor/ProcessingStep - simple types that
- implement a validating annotation processor
- defer invalid elements until later
- break processor actions into multiple steps (which may each handle different annotations)

## Usage/Setup

Auto common utilities have a standard maven setup which can be used from Gradle, Ivy, Ant, or other systems which consume binary artifacts from the central maven repositories.
Auto common utilities have a standard [Maven](http://maven.apache.org) setup which can also be
used from Gradle, Ivy, Ant, or other systems which consume binary artifacts from the central Maven
binary artifact repositories.

```xml
<dependency>
Expand All @@ -23,3 +31,49 @@ Auto common utilities have a standard maven setup which can be used from Gradle,
<version>1.0-SNAPSHOT</version> <!-- or use a known release version -->
</dependency>
```

## Processor Resilience

Auto Common Utilities is used by a variety of annotation processors in Google and new versions
may have breaking changes. Users of auto-common are urged to use
[shade](https://maven.apache.org/plugins/maven-shade-plugin/) or
[jarjar](https://code.google.com/p/jarjar/) (or something similar) in packaging their processors
so that conflicting versions of this library do not adversely interact with each other.

For example, in a Maven build you can repackage `com.google.auto.common` into
`your.processor.shaded.auto.common` like this:

```xml
<project>
<!-- your other config -->
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<!-- exclude dependencies you don't want to bundle in your processor -->
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>com.google.auto.common</pattern>
<shadedPattern>your.processor.shaded.auto.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
```

Expand Up @@ -22,6 +22,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.testing.compile.JavaFileObjects;

import org.junit.Test;
Expand All @@ -38,15 +39,17 @@
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;

@RunWith(JUnit4.class)
public class BasicAnnotationProcessorTest {

private Set<Element> elementsGeneratingCode = Sets.newHashSet();

@Retention(RetentionPolicy.SOURCE)
public @interface RequiresGeneratedCode {}

/** Asserts that the code generated by {@link GeneratesCode} and its processor is present. */
public static class RequiresGeneratedCodeProcessor extends BasicAnnotationProcessor {
public class RequiresGeneratedCodeProcessor extends BasicAnnotationProcessor {
boolean processed = false;

@Override
Expand All @@ -61,12 +64,7 @@ protected Iterable<? extends ProcessingStep> initSteps() {
public void process(
SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
processed = true;
try {
processingEnv.getFiler()
.getResource(StandardLocation.SOURCE_OUTPUT, "test", "SomeGeneratedClass");
} catch (IOException e) {
throw new AssertionError(e);
}
assertThat(elementsGeneratingCode).isNotEmpty();
}

@Override
Expand All @@ -81,7 +79,8 @@ public Set<? extends Class<? extends Annotation>> annotations() {
public @interface GeneratesCode {}

/** Generates a class called {@code test.SomeGeneratedClass}. */
public static class GeneratesCodeProcessor extends BasicAnnotationProcessor {
public class GeneratesCodeProcessor extends BasicAnnotationProcessor {

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
Expand Down Expand Up @@ -109,7 +108,9 @@ public Set<? extends Class<? extends Annotation>> annotations() {
});
}

// TODO(gak): Use jimfs to simulate the file system.
private void generateClass(Element sourceType) throws IOException {
elementsGeneratingCode.add(sourceType);
JavaFileObject source =
processingEnv.getFiler().createSourceFile("test.SomeGeneratedClass", sourceType);
PrintWriter writer = new PrintWriter(source.openWriter());
Expand Down
Expand Up @@ -24,6 +24,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimaps;
Expand Down Expand Up @@ -97,25 +98,30 @@ private void doProcess(RoundEnvironment roundEnv) {

ImmutableListMultimap.Builder<String, FactoryMethodDescriptor> indexedMethods =
ImmutableListMultimap.builder();
ImmutableSet.Builder<ImplementationMethodDescriptor> implementationMethodDescriptors =
ImmutableSet.builder();
ImmutableSetMultimap.Builder<String, ImplementationMethodDescriptor>
implementationMethodDescriptorsBuilder = ImmutableSetMultimap.builder();
for (Element element : roundEnv.getElementsAnnotatedWith(AutoFactory.class)) {
Optional<AutoFactoryDeclaration> declaration = declarationFactory.createIfValid(element);
if (declaration.isPresent()) {
String factoryName = declaration.get().getFactoryName(
elements.getPackageOf(element).getQualifiedName(),
getAnnotatedType(element).getSimpleName());

TypeElement extendingType = declaration.get().extendingType();
List<ExecutableElement> supertypeMethods =
ElementFilter.methodsIn(elements.getAllMembers(extendingType));
for (ExecutableElement supertypeMethod : supertypeMethods) {
if (supertypeMethod.getModifiers().contains(Modifier.ABSTRACT)) {
ExecutableType methodType = Elements2.getExecutableElementAsMemberOf(
types, supertypeMethod, extendingType);
implementationMethodDescriptors.add(new ImplementationMethodDescriptor.Builder()
.name(supertypeMethod.getSimpleName().toString())
.returnType(getAnnotatedType(element).getQualifiedName().toString())
.publicMethod()
.passedParameters(Parameter.forParameterList(
supertypeMethod.getParameters(), methodType.getParameterTypes()))
.build());
implementationMethodDescriptorsBuilder.put(factoryName,
new ImplementationMethodDescriptor.Builder()
.name(supertypeMethod.getSimpleName().toString())
.returnType(getAnnotatedType(element).getQualifiedName().toString())
.publicMethod()
.passedParameters(Parameter.forParameterList(
supertypeMethod.getParameters(), methodType.getParameterTypes()))
.build());
}
}
for (TypeElement implementingType : declaration.get().implementingTypes()) {
Expand All @@ -125,13 +131,14 @@ private void doProcess(RoundEnvironment roundEnv) {
if (interfaceMethod.getModifiers().contains(Modifier.ABSTRACT)) {
ExecutableType methodType = Elements2.getExecutableElementAsMemberOf(
types, interfaceMethod, implementingType);
implementationMethodDescriptors.add(new ImplementationMethodDescriptor.Builder()
.name(interfaceMethod.getSimpleName().toString())
.returnType(getAnnotatedType(element).getQualifiedName().toString())
.publicMethod()
.passedParameters(Parameter.forParameterList(
interfaceMethod.getParameters(), methodType.getParameterTypes()))
.build());
implementationMethodDescriptorsBuilder.put(factoryName,
new ImplementationMethodDescriptor.Builder()
.name(interfaceMethod.getSimpleName().toString())
.returnType(getAnnotatedType(element).getQualifiedName().toString())
.publicMethod()
.passedParameters(Parameter.forParameterList(
interfaceMethod.getParameters(), methodType.getParameterTypes()))
.build());
}
}
}
Expand All @@ -147,6 +154,9 @@ private void doProcess(RoundEnvironment roundEnv) {
}));
}

ImmutableSetMultimap<String, ImplementationMethodDescriptor>
implementationMethodDescriptors = implementationMethodDescriptorsBuilder.build();

for (Entry<String, Collection<FactoryMethodDescriptor>> entry
: indexedMethods.build().asMap().entrySet()) {
ImmutableSet.Builder<String> extending = ImmutableSet.builder();
Expand Down Expand Up @@ -180,8 +190,7 @@ private void doProcess(RoundEnvironment roundEnv) {
implementing.build(),
publicType,
ImmutableSet.copyOf(entry.getValue()),
// TODO(gak): this needs to be indexed too
implementationMethodDescriptors.build(),
implementationMethodDescriptors.get(entry.getKey()),
allowSubclasses));
} catch (IOException e) {
messager.printMessage(Kind.ERROR, "failed");
Expand Down

0 comments on commit e72ded3

Please sign in to comment.