blob: 2c8a3fb0fdaa3a84406f0d854f83107040525bd9 [file] [log] [blame]
/*
* Copyright 2015 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.auto.value.extension;
import com.google.common.collect.ImmutableSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
/**
* An AutoValueExtension allows for extra functionality to be created during the generation of an
* AutoValue class.
*
* <p>Extensions are discovered at compile time using the {@link java.util.ServiceLoader} APIs,
* allowing them to run without any additional annotations. To be found by {@code ServiceLoader}, an
* extension class must be public with a public no-arg constructor, and its fully-qualified name
* must appear in a file called {@code
* META-INF/services/com.google.auto.value.extension.AutoValueExtension} in a jar that is on the
* compiler's {@code -classpath} or {@code -processorpath}.
*
* <p>When the AutoValue processor runs for a class {@code Foo}, it will ask each Extension whether
* it is {@linkplain #applicable applicable}. Suppose two Extensions reply that they are. Then
* the processor will generate the AutoValue logic in a direct subclass of {@code Foo}, and it
* will ask the first Extension to generate a subclass of that, and the second Extension to generate
* a subclass of the subclass. So we might have this hierarchy:
*
* <pre>
* &#64;AutoValue abstract class Foo {...} // the hand-written class
* abstract class $$AutoValue_Foo extends Foo {...} // generated by AutoValue processor
* abstract class $AutoValue_Foo extends $$AutoValue_Foo {...} // generated by first Extension
* final class AutoValue_Foo extends $AutoValue_Foo {...} // generated by second Extension
* </pre>
*
* <p>(The exact naming scheme illustrated here is not fixed and should not be relied on.)
*
* <p>If an Extension needs its generated class to be the final class in the inheritance hierarchy,
* its {@link #mustBeFinal(Context)} method returns true. Only one Extension can return true for a
* given context. Only generated classes that will be the final class in the inheritance hierarchy
* can be declared final. All others should be declared abstract.
*
* <p>The first generated class in the hierarchy will always be the one generated by the AutoValue
* processor and the last one will always be the one generated by the Extension that {@code
* mustBeFinal}, if any. Other than that, the order of the classes in the hierarchy is unspecified.
* The last class in the hierarchy is {@code AutoValue_Foo} and that is the one that the
* {@code Foo} class will reference, for example with {@code new AutoValue_Foo(...)}.
*
* <p>Each Extension must also be sure to generate a constructor with arguments corresponding to all
* properties in {@link com.google.auto.value.extension.AutoValueExtension.Context#propertyTypes()},
* in order, and to call the superclass constructor with the same arguments. This constructor must
* have at least package visibility.
*
* <p>Because the class generated by the AutoValue processor is at the top of the generated
* hierarchy, Extensions can override its methods, for example {@code hashCode()},
* {@code toString()}, or the implementations of the various {@code bar()} property methods.
*/
public abstract class AutoValueExtension {
/** The context of the generation cycle. */
public interface Context {
/**
* Returns the processing environment of this generation cycle. This can be used, among other
* things, to produce compilation warnings or errors, using {@link
* ProcessingEnvironment#getMessager()}.
*/
ProcessingEnvironment processingEnvironment();
/** Returns the package name of the classes to be generated. */
String packageName();
/**
* Returns the annotated class that this generation cycle is based on.
*
* <p>Given {@code @AutoValue public class Foo {...}}, this will be {@code Foo}.
*/
TypeElement autoValueClass();
/**
* The fully-qualified name of the last class in the {@code AutoValue} hierarchy. For an
* {@code @AutoValue} class {@code foo.bar.Baz}, this will be {@code foo.bar.AutoValue_Baz}.
* The class may be generated by an extension, which will be the current extension if the
* {@code isFinal} parameter to {@link AutoValueExtension#generateClass} is true and the
* returned string is not {@code null}.
*
* <p>For compatibility reasons, this method has a default implementation that throws an
* exception. The AutoValue processor supplies an implementation that behaves as documented.
*/
default String finalAutoValueClassName() {
throw new UnsupportedOperationException();
}
/**
* Returns the ordered collection of properties to be generated by AutoValue. Each key is a
* property name, and the corresponding value is the getter method for that property. For
* example, if property {@code bar} is defined by {@code abstract String getBar()} then this map
* will have an entry mapping {@code "bar"} to the {@code ExecutableElement} for {@code
* getBar()}.
*
* <p>To determine the type of a property, it is best to use {@link #propertyTypes()} rather
* than looking at the return type of the {@link ExecutableElement} in this map. The reason is
* that the final type of the property might be different because of type variables. For
* example, if you have...
*
* <pre>
* interface Parent<T> {
* T bar();
* }
*
* {@code @AutoValue abstract class Foo implements Parent<String> {...}}
* </pre>
*
* ...then the type of the {@code bar} property in {@code Foo} is actually {@code String}, but
* the {@code ExecutableElement} will be the the method in {@code Parent}, whose return type is
* {@code T}.
*/
Map<String, ExecutableElement> properties();
/**
* Returns the properties to be generated by AutoValue, with their types. Each key is a property
* name, and the corresponding value is the type of that property. The order of the map entries
* is the same as the order of the {@code @AutoValue} properties.
*
* <p>For example, if property {@code bar} is defined by {@code abstract String getBar()} then
* this map will have an entry mapping {@code "bar"} to the {@code TypeMirror} for {@code
* String}.
*
* <p>For compatibility reasons, this method has a default implementation that throws an
* exception. The AutoValue processor supplies an implementation that behaves as documented.
*/
default Map<String, TypeMirror> propertyTypes() {
throw new UnsupportedOperationException();
}
/**
* Returns the complete set of abstract methods defined in or inherited by the
* {@code @AutoValue} class. This includes all methods that define properties (like {@code
* abstract String getBar()}), any abstract {@code toBuilder()} method, and any other abstract
* method even if it has been consumed by this or another Extension.
*/
Set<ExecutableElement> abstractMethods();
/**
* Returns a representation of the {@code Builder} associated with the {@code @AutoValue} class,
* if there is one.
*
* <p>This method returns {@link Optional#empty()} if called from within the {@link #applicable}
* method. If an Extension needs {@code Builder} information to decide whether it is applicable,
* it should return {@code true} from the {@link #applicable} method and then return {@code
* null} from the {@link #generateClass} method if it does not need to generate a class after
* all.
*
* <p>The default implementation of this method returns {@link Optional#empty()} for
* compatibility with extensions which may have implemented this interface themselves.
*/
default Optional<BuilderContext> builder() {
return Optional.empty();
}
}
/**
* Represents a {@code Builder} associated with an {@code @AutoValue} class.
*/
public interface BuilderContext {
/**
* Returns the {@code @AutoValue.Builder} interface or abstract class that this object
* represents.
*/
TypeElement builderType();
/**
* Returns abstract no-argument methods in the {@code @AutoValue} class that return the builder
* type.
*
* <p>Consider a class like this:
* <pre>
* {@code @AutoValue} abstract class Foo {
* abstract String bar();
*
* abstract Builder toBuilder();
*
* ...
* {@code @AutoValue.Builder}
* abstract static class Builder {...}
* }
* </pre>
*
* <p>Here {@code toBuilderMethods()} will return a set containing the method
* {@code Foo.toBuilder()}.
*/
Set<ExecutableElement> toBuilderMethods();
/**
* Returns static no-argument methods in the {@code @AutoValue} class that return the builder
* type.
*
* <p>Consider a class like this:
* <pre>
* {@code @AutoValue} abstract class Foo {
* abstract String bar();
*
* static Builder builder() {
* return new AutoValue_Foo.Builder()
* .setBar("default bar");
* }
*
* {@code @AutoValue.Builder}
* abstract class Builder {
* abstract Builder setBar(String x);
* abstract Foo build();
* }
* }
* </pre>
*
* <p>Here {@code builderMethods()} will return a set containing the method
* {@code Foo.builder()}. Generated code should usually call this method in preference to
* constructing {@code AutoValue_Foo.Builder()} directly, because this method can establish
* default values for properties, as it does here.
*/
Set<ExecutableElement> builderMethods();
/**
* Returns the method {@code build()} in the builder class, if it exists and returns the
* {@code @AutoValue} type. This is the method that generated code for
* {@code @AutoValue class Foo} should call in order to get an instance of {@code Foo} from its
* builder. The returned method is called {@code build()}; if the builder uses some other name
* then extensions have no good way to guess how they should build.
*
* <p>A common convention is for {@code build()} to be a concrete method in the
* {@code @AutoValue.Builder} class, which calls an abstract method {@code autoBuild()} that is
* implemented in the generated subclass. The {@code build()} method can then do validation,
* defaulting, and so on.
*/
Optional<ExecutableElement> buildMethod();
/**
* Returns the abstract build method. If the {@code @AutoValue} class is {@code Foo}, this is an
* abstract no-argument method in the builder class that returns {@code Foo}. This might be
* called {@code build()}, or, following a common convention, it might be called
* {@code autoBuild()} and used in the implementation of a {@code build()} method that is
* defined in the builder class.
*
* <p>Extensions should call the {@code build()} method in preference to this one. But they
* should override this one if they want to customize build-time behaviour.
*/
ExecutableElement autoBuildMethod();
/**
* Returns a map from property names to the corresponding setters. A property may have more than
* one setter. For example, an {@code ImmutableList<String>} might be set by
* {@code setFoo(ImmutableList<String>)} and {@code setFoo(String[])}.
*/
Map<String, Set<ExecutableElement>> setters();
/**
* Returns a map from property names to property builders. For example, if there is a property
* {@code foo} defined by {@code abstract ImmutableList<String> foo();} or
* {@code abstract ImmutableList<String> getFoo();} in the {@code @AutoValue} class,
* then there can potentially be a builder defined by
* {@code abstract ImmutableList.Builder<String> fooBuilder();} in the
* {@code @AutoValue.Builder} class. This map would then map {@code "foo"} to the
* {@link ExecutableElement} representing {@code fooBuilder()}.
*/
Map<String, ExecutableElement> propertyBuilders();
}
/**
* Indicates to an annotation processor environment supporting incremental annotation processing
* (currently a feature specific to Gradle starting with version 4.8) the incremental type of an
* Extension.
*
* <p>The constants for this enum are ordered by increasing performance (but also constraints).
*
* @see <a
* href="https://docs.gradle.org/current/userguide/java_plugin.html#sec:incremental_annotation_processing">Gradle
* documentation of its incremental annotation processing</a>
*/
public enum IncrementalExtensionType {
/**
* The incrementality of this extension is unknown, or it is neither aggregating nor isolating.
*/
UNKNOWN,
/**
* This extension is <i>aggregating</i>, meaning that it may generate outputs based on several
* annotated input classes and it respects the constraints imposed on aggregating processors.
* It is unusual for AutoValue extensions to be aggregating.
*
* @see <a
* href="https://docs.gradle.org/current/userguide/java_plugin.html#aggregating_annotation_processors">Gradle
* definition of aggregating processors</a>
*/
AGGREGATING,
/**
* This extension is <i>isolating</i>, meaning roughly that its output depends on the
* {@code @AutoValue} class and its dependencies, but not on other {@code @AutoValue} classes
* that might be compiled at the same time. The constraints that an isolating extension must
* respect are the same as those that Gradle imposes on an isolating annotation processor.
*
* @see <a
* href="https://docs.gradle.org/current/userguide/java_plugin.html#isolating_annotation_processors">Gradle
* definition of isolating processors</a>
*/
ISOLATING
}
/**
* Determines the incremental type of this Extension.
*
* <p>The {@link ProcessingEnvironment} can be used, among other things, to obtain the processor
* options, using {@link ProcessingEnvironment#getOptions()}.
*
* <p>The actual incremental type of the AutoValue processor as a whole will be the loosest
* incremental types of the Extensions present in the annotation processor path. The default
* returned value is {@link IncrementalExtensionType#UNKNOWN}, which will disable incremental
* annotation processing entirely.
*/
public IncrementalExtensionType incrementalType(ProcessingEnvironment processingEnvironment) {
return IncrementalExtensionType.UNKNOWN;
}
/**
* Analogous to {@link Processor#getSupportedOptions()}, here to allow extensions to report their
* own.
*
* <p>By default, if the extension class is annotated with {@link SupportedOptions}, this will
* return a set with the strings in the annotation. If the class is not so annotated, an empty set
* is returned.
*
* @return the set of options recognized by this extension or an empty set if none
* @see SupportedOptions
*/
public Set<String> getSupportedOptions() {
SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class);
if (so == null) {
return ImmutableSet.of();
} else {
return ImmutableSet.copyOf(so.value());
}
}
/**
* Determines whether this Extension applies to the given context. If an Extension returns {@code
* false} for a given class, it will not be called again during the processing of that class. An
* Extension can return {@code true} and still choose not to generate any code for the class, by
* returning {@code null} from {@link #generateClass}. That is often a more flexible approach.
*
* @param context The Context of the code generation for this class.
*/
public boolean applicable(Context context) {
return false;
}
/**
* Denotes that the class generated by this Extension must be the final class in the inheritance
* hierarchy. Only one Extension may be the final class, so this should be used sparingly.
*
* @param context the Context of the code generation for this class.
*/
public boolean mustBeFinal(Context context) {
return false;
}
/**
* Returns a possibly empty set of property names that this Extension intends to implement. This
* will prevent AutoValue from generating an implementation, and remove the supplied properties
* from builders, constructors, {@code toString}, {@code equals}, and {@code hashCode}. The
* default set returned by this method is empty.
*
* <p>Each returned string must be one of the property names in {@link Context#properties()}.
*
* <p>Returning a property name from this method is equivalent to returning the property's getter
* method from {@link #consumeMethods}.
*
* <p>For example, Android's {@code Parcelable} interface includes a <a
* href="http://developer.android.com/reference/android/os/Parcelable.html#describeContents()">method</a>
* {@code int describeContents()}. Since this is an abstract method with no parameters, by default
* AutoValue will consider that it defines an {@code int} property called {@code
* describeContents}. If an {@code @AutoValue} class implements {@code Parcelable} and does not
* provide an implementation of this method, by default its implementation will include {@code
* describeContents} in builders, constructors, and so on. But an {@code AutoValueExtension} that
* understands {@code Parcelable} can instead provide a useful implementation and return a set
* containing {@code "describeContents"}. Then {@code describeContents} will be omitted from
* builders and the rest.
*
* @param context the Context of the code generation for this class.
*/
public Set<String> consumeProperties(Context context) {
return ImmutableSet.of();
}
/**
* Returns a possible empty set of abstract methods that this Extension intends to implement. This
* will prevent AutoValue from generating an implementation, in cases where it would have, and it
* will also avoid warnings about abstract methods that AutoValue doesn't expect. The default set
* returned by this method is empty.
*
* <p>Each returned method must be one of the abstract methods in {@link
* Context#abstractMethods()}.
*
* <p>For example, Android's {@code Parcelable} interface includes a <a
* href="http://developer.android.com/reference/android/os/Parcelable.html#writeToParcel(android.os.Parcel,
* int)">method</a> {@code void writeToParcel(Parcel, int)}. Normally AutoValue would not know
* what to do with that abstract method. But an {@code AutoValueExtension} that understands {@code
* Parcelable} can provide a useful implementation and return the {@code writeToParcel} method
* here. That will prevent a warning about the method from AutoValue.
*
* @param context the Context of the code generation for this class.
*/
public Set<ExecutableElement> consumeMethods(Context context) {
return ImmutableSet.of();
}
/**
* Returns the generated source code of the class named {@code className} to extend {@code
* classToExtend}, or {@code null} if this extension does not generate a class in the hierarchy.
* If there is a generated class, it should be final if {@code isFinal} is true; otherwise it
* should be abstract. The returned string should be a complete Java class definition of the class
* {@code className} in the package {@link Context#packageName() context.packageName()}.
*
* <p>The returned string will typically look like this:
*
* <pre>{@code
* package <package>;
* ...
* <finalOrAbstract> class <className> extends <classToExtend> {
* // Constructor
* <className>(<constructorParameters>) {
* super(<constructorParameterNames>);
* ...
* }
* ...
* }}</pre>
*
* <p>Here, {@code <package>} is {@link Context#packageName()}; {@code <finalOrAbstract>} is the
* keyword {@code final} if {@code isFinal} is true or {@code abstract} otherwise; and {@code
* <className>} and {@code <classToExtend>} are the values of this method's parameters of the same
* name. The {@code <constructorParameters>} and {@code <constructorParameterNames>} are typically
* derived from {@link Context#propertyTypes()}.
*
* @param context The {@link Context} of the code generation for this class.
* @param className The simple name of the resulting class. The returned code will be written to a
* file named accordingly.
* @param classToExtend The simple name of the direct parent of the generated class. This could be
* the AutoValue generated class, or a class generated as the result of another Extension.
* @param isFinal True if this class is the last class in the chain, meaning it should be marked
* as final. Otherwise it should be marked as abstract.
* @return The source code of the generated class, or {@code null} if this extension does not
* generate a class in the hierarchy.
*/
public abstract String generateClass(
Context context, String className, String classToExtend, boolean isFinal);
}