Search in sources :

Example 1 with AutoValueExtension

use of com.google.auto.value.extension.AutoValueExtension in project auto by google.

the class AutoValueProcessor method processType.

private void processType(TypeElement type) {
    AutoValue autoValue = type.getAnnotation(AutoValue.class);
    if (autoValue == null) {
        // This shouldn't happen unless the compilation environment is buggy,
        // but it has happened in the past and can crash the compiler.
        errorReporter.abortWithError("annotation processor for @AutoValue was invoked with a type" + " that does not have that annotation; this is probably a compiler bug", type);
    }
    if (type.getKind() != ElementKind.CLASS) {
        errorReporter.abortWithError("@" + AutoValue.class.getName() + " only applies to classes", type);
    }
    if (ancestorIsAutoValue(type)) {
        errorReporter.abortWithError("One @AutoValue class may not extend another", type);
    }
    if (implementsAnnotation(type)) {
        errorReporter.abortWithError("@AutoValue may not be used to implement an annotation" + " interface; try using @AutoAnnotation instead", type);
    }
    checkModifiersIfNested(type);
    // We are going to classify the methods of the @AutoValue class into several categories.
    // This covers the methods in the class itself and the ones it inherits from supertypes.
    // First, the only concrete (non-abstract) methods we are interested in are overrides of
    // Object methods (equals, hashCode, toString), which signal that we should not generate
    // an implementation of those methods.
    // Then, each abstract method is one of the following:
    // (1) A property getter, like "abstract String foo()" or "abstract String getFoo()".
    // (2) A toBuilder() method, which is any abstract no-arg method returning the Builder for
    //     this @AutoValue class.
    // (3) An abstract method that will be consumed by an extension, such as
    //     Parcelable.describeContents() or Parcelable.writeToParcel(Parcel, int).
    // The describeContents() example shows a quirk here: initially we will identify it as a
    // property, which means that we need to reconstruct the list of properties after allowing
    // extensions to consume abstract methods.
    // If there are abstract methods that don't fit any of the categories above, that is an error
    // which we signal explicitly to avoid confusion.
    ImmutableSet<ExecutableElement> methods = getLocalAndInheritedMethods(type, processingEnv.getTypeUtils(), processingEnv.getElementUtils());
    ImmutableSet<ExecutableElement> abstractMethods = abstractMethodsIn(methods);
    BuilderSpec builderSpec = new BuilderSpec(type, processingEnv, errorReporter);
    Optional<BuilderSpec.Builder> builder = builderSpec.getBuilder();
    ImmutableSet<ExecutableElement> toBuilderMethods;
    if (builder.isPresent()) {
        toBuilderMethods = builder.get().toBuilderMethods(typeUtils, abstractMethods);
    } else {
        toBuilderMethods = ImmutableSet.of();
    }
    ImmutableSet<ExecutableElement> propertyMethods = propertyMethodsIn(immutableSetDifference(abstractMethods, toBuilderMethods));
    ImmutableBiMap<String, ExecutableElement> properties = propertyNameToMethodMap(propertyMethods);
    ExtensionContext context = new ExtensionContext(processingEnv, type, properties, abstractMethods);
    ImmutableList<AutoValueExtension> applicableExtensions = applicableExtensions(type, context);
    ImmutableSet<ExecutableElement> consumedMethods = methodsConsumedByExtensions(type, applicableExtensions, context, abstractMethods, properties);
    if (!consumedMethods.isEmpty()) {
        ImmutableSet<ExecutableElement> allAbstractMethods = abstractMethods;
        abstractMethods = immutableSetDifference(abstractMethods, consumedMethods);
        toBuilderMethods = immutableSetDifference(toBuilderMethods, consumedMethods);
        propertyMethods = propertyMethodsIn(immutableSetDifference(abstractMethods, toBuilderMethods));
        properties = propertyNameToMethodMap(propertyMethods);
        context = new ExtensionContext(processingEnv, type, properties, allAbstractMethods);
    }
    boolean extensionsPresent = !applicableExtensions.isEmpty();
    validateMethods(type, abstractMethods, toBuilderMethods, propertyMethods, extensionsPresent);
    String finalSubclass = generatedSubclassName(type, 0);
    AutoValueTemplateVars vars = new AutoValueTemplateVars();
    vars.pkg = TypeSimplifier.packageNameOf(type);
    vars.origClass = TypeSimplifier.classNameOf(type);
    vars.simpleClassName = TypeSimplifier.simpleNameOf(vars.origClass);
    vars.finalSubclass = TypeSimplifier.simpleNameOf(finalSubclass);
    vars.types = processingEnv.getTypeUtils();
    determineObjectMethodsToGenerate(methods, vars);
    TypeSimplifier typeSimplifier = defineVarsForType(type, vars, toBuilderMethods, propertyMethods, builder);
    // Only copy annotations from a class if it has @AutoValue.CopyAnnotations.
    if (isAnnotationPresent(type, AutoValue.CopyAnnotations.class)) {
        Set<String> excludedAnnotations = union(getFieldOfClasses(type, AutoValue.CopyAnnotations.class, "exclude", processingEnv.getElementUtils()), getAnnotationsMarkedWithInherited(type));
        vars.annotations = copyAnnotations(type, typeSimplifier, excludedAnnotations);
    } else {
        vars.annotations = ImmutableList.of();
    }
    GwtCompatibility gwtCompatibility = new GwtCompatibility(type);
    vars.gwtCompatibleAnnotation = gwtCompatibility.gwtCompatibleAnnotationString();
    int subclassDepth = writeExtensions(type, context, applicableExtensions);
    String subclass = generatedSubclassName(type, subclassDepth);
    vars.subclass = TypeSimplifier.simpleNameOf(subclass);
    vars.isFinal = (subclassDepth == 0);
    String text = vars.toText();
    text = Reformatter.fixup(text);
    writeSourceFile(subclass, text, type);
    GwtSerialization gwtSerialization = new GwtSerialization(gwtCompatibility, processingEnv, type);
    gwtSerialization.maybeWriteGwtSerializer(vars);
}
Also used : ExecutableElement(javax.lang.model.element.ExecutableElement) AutoValueExtension(com.google.auto.value.extension.AutoValueExtension) AutoValue(com.google.auto.value.AutoValue)

Example 2 with AutoValueExtension

use of com.google.auto.value.extension.AutoValueExtension in project auto by google.

the class AutoValueProcessor method writeExtensions.

// Invokes each of the given extensions to generate its subclass, and returns the number of
// hierarchy classes that extensions generated. This number is then the number of $ characters
// that should precede the name of the AutoValue implementation class.
// Assume the @AutoValue class is com.example.Foo.Bar. Then if there are no
// extensions the returned value will be 0, so the AutoValue implementation will be
// com.example.AutoValue_Foo_Bar. If there is one extension, it will be asked to
// generate AutoValue_Foo_Bar with parent $AutoValue_Foo_Bar. If it does so (returns
// non-null) then the returned value will be 1, so the AutoValue implementation will be
// com.example.$AutoValue_Foo_Bar. Otherwise, the returned value will still be 0. Likewise,
// if there is a second extension and both extensions return non-null, the first one will
// generate AutoValue_Foo_Bar with parent $AutoValue_Foo_Bar, the second will generate
// $AutoValue_Foo_Bar with parent $$AutoValue_Foo_Bar, and the returned value will be 2 for
// com.example.$$AutoValue_Foo_Bar.
private int writeExtensions(TypeElement type, ExtensionContext context, ImmutableList<AutoValueExtension> applicableExtensions) {
    int writtenSoFar = 0;
    for (AutoValueExtension extension : applicableExtensions) {
        String parentFqName = generatedSubclassName(type, writtenSoFar + 1);
        String parentSimpleName = TypeSimplifier.simpleNameOf(parentFqName);
        String classFqName = generatedSubclassName(type, writtenSoFar);
        String classSimpleName = TypeSimplifier.simpleNameOf(classFqName);
        boolean isFinal = (writtenSoFar == 0);
        String source = extension.generateClass(context, classSimpleName, parentSimpleName, isFinal);
        if (source != null) {
            source = Reformatter.fixup(source);
            writeSourceFile(classFqName, source, type);
            writtenSoFar++;
        }
    }
    return writtenSoFar;
}
Also used : AutoValueExtension(com.google.auto.value.extension.AutoValueExtension)

Example 3 with AutoValueExtension

use of com.google.auto.value.extension.AutoValueExtension in project auto by google.

the class AutoValueProcessor method methodsConsumedByExtensions.

private ImmutableSet<ExecutableElement> methodsConsumedByExtensions(TypeElement type, ImmutableList<AutoValueExtension> applicableExtensions, ExtensionContext context, ImmutableSet<ExecutableElement> abstractMethods, ImmutableBiMap<String, ExecutableElement> properties) {
    Set<ExecutableElement> consumed = Sets.newHashSet();
    for (AutoValueExtension extension : applicableExtensions) {
        Set<ExecutableElement> consumedHere = Sets.newHashSet();
        for (String consumedProperty : extension.consumeProperties(context)) {
            ExecutableElement propertyMethod = properties.get(consumedProperty);
            if (propertyMethod == null) {
                errorReporter.reportError("Extension " + extensionName(extension) + " wants to consume a property that does not exist: " + consumedProperty, type);
            } else {
                consumedHere.add(propertyMethod);
            }
        }
        for (ExecutableElement consumedMethod : extension.consumeMethods(context)) {
            if (!abstractMethods.contains(consumedMethod)) {
                errorReporter.reportError("Extension " + extensionName(extension) + " wants to consume a method that is not one of the abstract methods in this" + " class: " + consumedMethod, type);
            } else {
                consumedHere.add(consumedMethod);
            }
        }
        for (ExecutableElement repeat : Sets.intersection(consumed, consumedHere)) {
            errorReporter.reportError("Extension " + extensionName(extension) + " wants to consume a method that was already" + " consumed by another extension", repeat);
        }
        consumed.addAll(consumedHere);
    }
    return ImmutableSet.copyOf(consumed);
}
Also used : ExecutableElement(javax.lang.model.element.ExecutableElement) AutoValueExtension(com.google.auto.value.extension.AutoValueExtension)

Example 4 with AutoValueExtension

use of com.google.auto.value.extension.AutoValueExtension in project auto by google.

the class AutoValueProcessor method applicableExtensions.

private ImmutableList<AutoValueExtension> applicableExtensions(TypeElement type, ExtensionContext context) {
    List<AutoValueExtension> applicableExtensions = Lists.newArrayList();
    List<AutoValueExtension> finalExtensions = Lists.newArrayList();
    for (AutoValueExtension extension : extensions) {
        if (extension.applicable(context)) {
            if (extension.mustBeFinal(context)) {
                finalExtensions.add(extension);
            } else {
                applicableExtensions.add(extension);
            }
        }
    }
    switch(finalExtensions.size()) {
        case 0:
            break;
        case 1:
            applicableExtensions.add(0, finalExtensions.get(0));
            break;
        default:
            errorReporter.reportError("More than one extension wants to generate the final class: " + FluentIterable.from(finalExtensions).transform(ExtensionName.INSTANCE).join(Joiner.on(", ")), type);
            break;
    }
    return ImmutableList.copyOf(applicableExtensions);
}
Also used : AutoValueExtension(com.google.auto.value.extension.AutoValueExtension)

Example 5 with AutoValueExtension

use of com.google.auto.value.extension.AutoValueExtension in project auto by google.

the class ExtensionTest method testCantConsumeTwice.

@Test
public void testCantConsumeTwice() throws Exception {
    class ConsumeDizzle extends NonFinalExtension {

        @Override
        public Set<String> consumeProperties(Context context) {
            return ImmutableSet.of("dizzle");
        }
    }
    class AlsoConsumeDizzle extends ConsumeDizzle {
    }
    AutoValueExtension ext1 = new ConsumeDizzle();
    AutoValueExtension ext2 = new AlsoConsumeDizzle();
    Truth.assertThat(ext1).isNotEqualTo(ext2);
    JavaFileObject impl = JavaFileObjects.forSourceLines("foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue public abstract class Baz {", "  abstract String foo();", "  abstract String dizzle();", "}");
    assertThat(impl).processedWith(new AutoValueProcessor(ImmutableList.of(ext1, ext2))).failsToCompile().withErrorContaining("wants to consume a method that was already consumed").in(impl).onLine(5);
}
Also used : JavaFileObject(javax.tools.JavaFileObject) AutoValueExtension(com.google.auto.value.extension.AutoValueExtension) Test(org.junit.Test)

Aggregations

AutoValueExtension (com.google.auto.value.extension.AutoValueExtension)5 ExecutableElement (javax.lang.model.element.ExecutableElement)2 AutoValue (com.google.auto.value.AutoValue)1 JavaFileObject (javax.tools.JavaFileObject)1 Test (org.junit.Test)1