Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge from moe writing branch from 061401e8f8d2b201f88f2bfd7509a49b2433685c #235

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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