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);
}
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;
}
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);
}
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);
}
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);
}
Aggregations