Share
Embed
Annotation Processing Boilerplate Destruction (Droidcon NYC 2014)
The Java programming language has a knack for requiring a lot of boilerplate code. Annotation processing is a feature of the Java compiler which provides hooks that allow automatic code generation based on annotations. This greatly simplifies your code by pushing the burden of the boilerplate on automated tooling.
This talk will briefly showcase existing annotation processors for Android development. We will then focus on how to get started writing your own annotation processor.
Jake Wharton PRO
September 20, 2014
Transcript
212 slides
-
Dagger class CoffeeMaker { @Inject Lazy<Heater> heater; @Inject Pump pump;
public void brew() { heater.get().on(); pump.pump(); System.out.println(" [_]P coffee! [_]P "); heater.get().off(); } } square.github.io/dagger/ -
Butter Knife class SimpleActivity extends Activity { @InjectView(R.id.title) TextView title;
@InjectView(R.id.subtitle) TextView subtitle; @InjectView(R.id.hello) Button hello; @InjectView(R.id.list_of_things) ListView listOfThings; @InjectView(R.id.footer) TextView footer; // ... } jakewharton.github.io/butterknife/ -
Butter Knife class SimpleActivity extends Activity { @InjectView(R.id.title) TextView title;
@InjectView(R.id.subtitle) TextView subtitle; @InjectView(R.id.hello) Button hello; @InjectView(R.id.list_of_things) ListView listOfThings; @InjectView(R.id.footer) TextView footer; // ... } jakewharton.github.io/butterknife/ ButterKnife.inject(this); -
AutoValue @AutoValue abstract static class Animal { static Animal create(String
name, int numberOfLegs) { return new AutoValue_Animal(name, numberOfLegs); } abstract String name(); abstract int numberOfLegs(); } github.com/google/auto/ -
AutoParcel @AutoParcel abstract class SomeModel implements Parcelable { abstract String
name(); abstract List<SomeSubModel> subModels(); abstract Map<String, OtherSubModel> modelsMap(); static SomeModel create(String name, List<SomeSubModel> subModels, Map<String, OtherSubModel> modelsMap) { return new AutoParcel_SomeModel(name, subModels, modelsMap); } } github.com/frankiesardo/auto-parcel/ -
Schematic interface NoteColumns { @DataType(INTEGER) @PrimaryKey @AutoIncrement String _ID
= "_id"; @DataType(INTEGER) @References(table = NotesDatabase.LISTS, column = ListColumns._ID) String LIST_ID = "listId"; @DataType(TEXT) String NOTE = "note"; } -
Schematic @Database(version = NotesDatabase.VERSION) public final class NotesDatabase {
public static final int VERSION = 1; @Table(ListColumns.class) public static final String LISTS = "lists"; @Table(NoteColumns.class) public static final String NOTES = "notes"; } -
Schematic @ContentProvider(authority = NotesProvider.AUTHORITY, database = NotesDatabase.class) public final class
NotesProvider { // ... } github.com/SimonVT/schematic -
Doclet import com.sun.javadoc.*; ! public class ExampleDoclet { public static
boolean start(RootDoc root) { ! ! ! } } http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/doclet/overview.html -
Doclet import com.sun.javadoc.*; ! public class ExampleDoclet { public static
boolean start(RootDoc root) { ! ! ! } } import com.sun.javadoc.*; ! public class ExampleDoclet { public static boolean start(RootDoc root) { ClassDoc[] classes = root.classes(); for (int i = 0; i < classes.length; i++) { System.out.println(classes[i]); } return true; } } http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/doclet/overview.html -
Doclet import com.sun.javadoc.*; ! public class ExampleDoclet { public static
boolean start(RootDoc root) { ! ! ! } } import com.sun.javadoc.*; ! public class ExampleDoclet { public static boolean start(RootDoc root) { ClassDoc[] classes = root.classes(); for (int i = 0; i < classes.length; i++) { System.out.println(classes[i]); } return true; } } javadoc -doclet ExampleDoclet -docletpath . RandomClass.java \ FooBarClass.java HelloClass.java WorldClass.java import com.sun.javadoc.*; ! public class ExampleDoclet { public static boolean start(RootDoc root) { ClassDoc[] classes = root.classes(); for (int i = 0; i < classes.length; i++) { System.out.println(classes[i]); } return true; } } http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/doclet/overview.html -
Java 1.5 • JSR 14: Generics • JSR 175: Annotations
• Also a whole bunch of other really awesome things! -
Java 1.5 • JSR 14: Generics • JSR 175: Annotations
• Also a whole bunch of other really awesome things! • apt command-line tool -
@Target(ElementType.TYPE) public @interface Example { } @Example public class RandomClass
{ // ... } @Example public class FooBarClass { // ... } @Example public class HelloClass { // ... } @Example public class WorldClass { // ... } @Target(ElementType.TYPE) public @interface Example { } apt -
interface AnnotationProcessorFactory { AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env);
Collection<String> supportedAnnotationTypes(); Collection<String> supportedOptions(); } interface AnnotationProcessor { void process(); } apt -
apt public class ExampleAnnotationProcessorFactory implements AnnotationProcessorFactory {
@Override public Collection<String> supportedOptions() { return Collections.emptyList(); } } -
apt public class ExampleAnnotationProcessorFactory implements AnnotationProcessorFactory {
@Override public Collection<String> supportedAnnotationTypes() { return Collections.singletonList(Example.class.getName()); } @Override public Collection<String> supportedOptions() { return Collections.emptyList(); } } -
apt public class ExampleAnnotationProcessorFactory implements AnnotationProcessorFactory { @Override public
AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) { return new ExampleAnnotationProcessor(atds, env); } @Override public Collection<String> supportedAnnotationTypes() { return Collections.singletonList(Example.class.getName()); } @Override public Collection<String> supportedOptions() { return Collections.emptyList(); } } -
apt public class ExampleAnnotationProcessorFactory implements AnnotationProcessorFactory { @Override public
AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) { return new ExampleAnnotationProcessor(atds, env); } @Override public Collection<String> supportedAnnotationTypes() { return Collections.singletonList(Example.class.getName()); } @Override public Collection<String> supportedOptions() { return Collections.emptyList(); } } -
apt public class
} public class ExampleAnnotationProcessor implements AnnotationProcessor { private final Set<AnnotationTypeDeclaration> atds; private final AnnotationProcessorEnvironment env; public ExampleAnnotationProcessor( Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env) { this.atds = atds; this.env = env; } } -
apt public class
} public class Set<AnnotationTypeDeclaration> atds } public class ExampleAnnotationProcessor implements AnnotationProcessor { } public class ExampleAnnotationProcessor implements AnnotationProcessor { // ... } public class ExampleAnnotationProcessor implements AnnotationProcessor { // ... @Override public void process() { ! ! } ! } -
apt public class
} public class Set<AnnotationTypeDeclaration> atds } public class ExampleAnnotationProcessor implements AnnotationProcessor { } public class ExampleAnnotationProcessor implements AnnotationProcessor { // ... } public class ExampleAnnotationProcessor implements AnnotationProcessor { // ... @Override public void process() { for (AnnotationTypeDeclaration atd : atds) { MemberDeclaration member = atd.getDeclaringType(); System.out.println(member); } } ! ! } -
apt apt -classpath out/ src/RandomClass.java \ src/FooBarClass.java src/HelloClass.java \ src/WorldClass.java
http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/apt.html -
JSR 269 • 6 months after Java 5 released •
“Pluggable Annotation Processing API” -
JSR 269 • 6 months after Java 5 released •
“Pluggable Annotation Processing API” • Google, Oracle, Sun, BEA Systems sponsored -
JSR 269 • 6 months after Java 5 released •
“Pluggable Annotation Processing API” • Google, Oracle, Sun, BEA Systems sponsored • Simplify and standardize processing annotations -
JSR 269 • 6 months after Java 5 released •
“Pluggable Annotation Processing API” • Google, Oracle, Sun, BEA Systems sponsored • Simplify and standardize processing annotations • No standalone tool, part of javac -
interface Processor { Set<String> getSupportedOptions(); Set<String> getSupportedAnnotationTypes(); SourceVersion
getSupportedSourceVersion(); void init(ProcessingEnvironment processingEnv); boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); } -
public class ExampleProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion()
{ return SourceVersion.latestSupported(); } } -
public class ExampleProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion()
{ return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { } } -
public class ExampleProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion()
{ return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { return Collections.singleton(Example.class.getName()); } } -
public class ExampleProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion()
{ return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { return Collections.singleton(Example.class.getName()); } @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { } } -
public class ExampleProcessor extends AbstractProcessor { // ... @Override public
boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { ! ! ! ! ! } } -
public class ExampleProcessor extends AbstractProcessor { // ... @Override
public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); } } -
public class ExampleProcessor extends AbstractProcessor { // ... @Override
public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element); } } } -
public class ExampleProcessor extends AbstractProcessor { // ... @Override
public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element); } return false; } } -
public class ExampleProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion()
{ return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { return Collections.singleton(Example.class.getName()); } @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element); } return false; } } -
Execution • Happens automatically when javac runs. • ServiceLoader finds
and creates Processor instances. -
Service Loader • META-‐INF/services/ • File name of implemented fully-qualified
class name. • javax.annotation.processing.Processor -
Service Loader • META-‐INF/services/ • File name of implemented fully-qualified
class name. • javax.annotation.processing.Processor • Each line implementation fully-qualified class name. -
Service Loader • META-‐INF/services/ • File name of implemented fully-qualified
class name. • javax.annotation.processing.Processor • Each line implementation fully-qualified class name. • com.example.ExampleProcessor -
Execution • Happens automatically when javac runs. • ServiceLoader finds
and creates Processor instances. -
Execution • Happens automatically when javac runs. • ServiceLoader finds
and creates Processor instances. • Inside a full JVM. -
Execution • Happens automatically when javac runs. • ServiceLoader finds
and creates Processor instances. • Inside a full JVM. • Compiled sources and its dependencies not available. -
Execution • Happens automatically when javac runs. • ServiceLoader finds
and creates Processor instances. • Inside a full JVM. • Compiled sources and its dependencies not available. • Bring your own dependencies. -
Environment interface Processor { Set<String> getSupportedOptions(); Set<String> getSupportedAnnotationTypes();
SourceVersion getSupportedSourceVersion(); void init(ProcessingEnvironment processingEnv); boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); } -
Environment interface Processor { Set<String> getSupportedOptions(); Set<String> getSupportedAnnotationTypes();
SourceVersion getSupportedSourceVersion(); void init(ProcessingEnvironment processingEnv); boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); } -
Environment interface Processor { Set<String> getSupportedOptions(); Set<String> getSupportedAnnotationTypes();
SourceVersion getSupportedSourceVersion(); void init(ProcessingEnvironment processingEnv); boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); } -
Environment interface Processor { Set<String> getSupportedOptions(); Set<String> getSupportedAnnotationTypes();
SourceVersion getSupportedSourceVersion(); void init(ProcessingEnvironment processingEnv); boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); } -
Environment interface Processor { Set<String> getSupportedOptions(); Set<String> getSupportedAnnotationTypes();
SourceVersion getSupportedSourceVersion(); void init(ProcessingEnvironment processingEnv); boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); } -
Processing Environment interface ProcessingEnvironment { Map<String, String> getOptions(); SourceVersion
getSourceVersion(); Locale getLocale(); Messager getMessager(); Filer getFiler(); Elements getElementUtils(); Types getTypeUtils(); } -
Processing Environment interface ProcessingEnvironment { Map<String, String> getOptions(); SourceVersion
getSourceVersion(); Locale getLocale(); Messager getMessager(); Filer getFiler(); Elements getElementUtils(); Types getTypeUtils(); } -
Processing Environment interface ProcessingEnvironment { Map<String, String> getOptions(); SourceVersion
getSourceVersion(); Locale getLocale(); Messager getMessager(); Filer getFiler(); Elements getElementUtils(); Types getTypeUtils(); } -
Processing Environment interface ProcessingEnvironment { Map<String, String> getOptions(); SourceVersion
getSourceVersion(); Locale getLocale(); Messager getMessager(); Filer getFiler(); Elements getElementUtils(); Types getTypeUtils(); } -
Processing Environment interface ProcessingEnvironment { Map<String, String> getOptions(); SourceVersion
getSourceVersion(); Locale getLocale(); Messager getMessager(); Filer getFiler(); Elements getElementUtils(); Types getTypeUtils(); } -
Elements @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } -
Elements @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } TypeElement -
Elements @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } TypeElement VariableElement -
Elements @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } TypeElement VariableElement ExecutableElement -
Elements @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } TypeElement VariableElement ExecutableElement ExecutableElement -
Elements @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } TypeElement VariableElement ExecutableElement ExecutableElement -
Elements @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } TypeElement VariableElement ExecutableElement ExecutableElement -
Types @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } -
Types @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } -
Types @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } -
Types @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } -
Types @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } -
Types @Example public class ExampleClass implements Runnable { private
final String name; public ExampleClass(String name) { this.name = name; } @Override public void run() { System.out.println("Hello, " + name); } } -
Environment public class ExampleProcessor extends AbstractProcessor { // ...
@Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element } return false; } } ); -
Environment public class ExampleProcessor extends AbstractProcessor { // ...
@Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element } return false; } } ); -
Environment public class ExampleProcessor extends AbstractProcessor { // ...
@Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println( ); } return false; } } element.asType() -
Environment public class ExampleProcessor extends AbstractProcessor { // ...
@Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element.asType()); } return false; } } -
Environment public class ExampleProcessor extends AbstractProcessor { // ...
@Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element.asType()); } return false; } } -
Environment public class ExampleProcessor extends AbstractProcessor { // ...
@Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element.asType()); } return false; } } -
Round Environment public interface RoundEnvironment { boolean processingOver(); boolean
errorRaised(); Set<? extends Element> getRootElements(); Set<? extends Element> getElementsAnnotatedWith( TypeElement a); Set<? extends Element> getElementsAnnotatedWith( Class<? extends Annotation> a); } -
Round Environment public interface RoundEnvironment { boolean processingOver(); boolean
errorRaised(); Set<? extends Element> getRootElements(); Set<? extends Element> getElementsAnnotatedWith( TypeElement a); Set<? extends Element> getElementsAnnotatedWith( Class<? extends Annotation> a); } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Example public class RandomClass { // ... } javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Example public class RandomClass { // ... } javac ExampleProcessor -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } @Example public class RandomClass { // ... } javac ExampleProcessor -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } @Example public class RandomClass { // ... } javac OtherProcessor -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } @Example public class RandomClass { // ... } javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } @Example public class RandomClass { // ... } javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } @Example public class RandomClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } @Example public class RandomClass { // ... } javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } @Example public class RandomClass { // ... } javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac ExampleProcessor -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac OtherProcessor -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac OtherProcessor public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } OtherGenClass.class public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } ExampleProcessor OtherGenClass.class public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } OtherProcessor OtherGenClass.class public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } OtherGenClass.class public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } javac public class OtherGenClass { // ... } OtherGenClass.class public class OtherGenClass { // ... } -
Processing Rounds @Example public class RandomClass { // ... }
ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other public class GeneratedClass { // ... } GeneratedClass.class @Example public class RandomClass { // ... } @Other public class GeneratedClass { // ... } public class OtherGenClass { // ... } OtherGenClass.class public class OtherGenClass { // ... } -
Parsing and Generation public class ExampleProcessor extends AbstractProcessor { //
... @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { } return false; } } -
Parsing and Generation public class ExampleProcessor extends AbstractProcessor { //
... @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { } return false; } } // Look at attributes of 'element'. -
Parsing and Generation public class ExampleProcessor extends AbstractProcessor { //
... @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { } return false; } } // Look at attributes of 'element'. // Generate some code based on attributes. -
Parsing and Generation @Override public boolean process( Set<? extends TypeElement>
annotations, RoundEnvironment env) { return false; } -
Parsing and Generation @Override public boolean process( Set<? extends TypeElement>
annotations, RoundEnvironment env) { return false; } List<ExampleModel> models = parseExampleAnnotations(env); -
Parsing and Generation @Override public boolean process( Set<? extends TypeElement>
annotations, RoundEnvironment env) { return false; } List<ExampleModel> models = parseExampleAnnotations(env); List<ExampleModel> parseExampleAnnotations(RoundEnvironment env) { // ... } -
Parsing and Generation @Override public boolean process( Set<? extends TypeElement>
annotations, RoundEnvironment env) { return false; } List<ExampleModel> models = parseExampleAnnotations(env); for (ExampleModel model : models) { emitExampleCode(model); } List<ExampleModel> parseExampleAnnotations(RoundEnvironment env) { // ... } -
Parsing and Generation @Override public boolean process( Set<? extends TypeElement>
annotations, RoundEnvironment env) { return false; } List<ExampleModel> models = parseExampleAnnotations(env); for (ExampleModel model : models) { emitExampleCode(model); } List<ExampleModel> parseExampleAnnotations(RoundEnvironment env) { // ... } ! ! void emitExampleCode(ExampleModel model) { // ... } -
Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”
public: true ExampleModel ! name: “Foo” package: “com.example” public: true element: -
Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”
public: true ExampleModel ! name: “Foo” package: “com.example” public: true element: GeneratedClass -
Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”
public: true ExampleModel ! name: “Foo” package: “com.example” public: true element: GeneratedClass -
Bring Your Own Dependencies • Guava, Guava, Guava! • JavaWriter
• google/auto ‘common’ -
Bring Your Own Dependencies • Guava, Guava, Guava! • JavaWriter
• google/auto ‘common’ • AutoService (google/auto) -
JavaWriter JavaFileObject sourceFile = filer.createSourceFile(name, element); ! ! ! JavaWriter
writer = new JavaWriter(sourceFile.openWriter()); writer.emitPackage("com.example") .emitAnnotation(Other.class) .beginType("GeneratedClass", "class") .emitSingleLineComment("...") .endType(); -
JavaWriter JavaFileObject sourceFile = filer.createSourceFile(name, element); ! ! @com.example.Other public
class GeneratedClass { // ... } ! ! ! JavaWriter writer = new JavaWriter(sourceFile.openWriter()); writer.emitPackage("com.example") .emitAnnotation(Other.class) .beginType("GeneratedClass", "class") .emitSingleLineComment("...") .endType(); -
JavaWriter JavaFileObject sourceFile = filer.createSourceFile(name, element); import com.example.Other; ! @Other
public class GeneratedClass { // ... } ! ! ! JavaWriter writer = new JavaWriter(sourceFile.openWriter()); writer.emitPackage("com.example") .emitImports(Other.class) .emitAnnotation(Other.class) .beginType("GeneratedClass", "class") .emitSingleLineComment("...") .endType(); JavaFileObject sourceFile = filer.createSourceFile(name, element); -
AutoService • Remember? META-‐INF/services/javax.annotation.processing.Processor • AutoService generates service files from
annotations ! public class ExampleProcessor extends AbstractProcessor { // ... } -
AutoService • Remember? META-‐INF/services/javax.annotation.processing.Processor • AutoService generates service files from
annotations ! public class ExampleProcessor extends AbstractProcessor { // ... } @AutoService(Processor.class) -
Testing and Debugging • Google to the rescue! • Guava
• Guava • compile-testing -
Testing and Debugging • Google to the rescue! • Guava
• Guava • compile-testing • Truth -
final class TestProcessors { static Iterable<? extends Processor> exampleProcessors() {
return Collections.singletonList( new ExampleProcessor() ); } } -
String input = Joiner.on('\n').join( "package test;", "import com.example.Example;", "@Example", "public
class ExampleClass {", "}" ); -
String input = Joiner.on('\n').join( "package test;", "import com.example.Example;", "@Example", "public
class ExampleClass {", "}" ); JavaFileObject inputFile = JavaFileObjects.forSourceString("test.Test", input); -
JavaFileObject inputFile = JavaFileObjects.forSourceString("test.Test", input); String input = """\ package
test; import com.example.Example; @Example public class ExampleClass { } """ .stripIndent() -
String expected = Joiner.on('\n').join( "package test;", "import com.example.Other;", "@Other", "public
class GeneratedClass {", "}" ); -
String expected = Joiner.on('\n').join( "package test;", "import com.example.Other;", "@Other", "public
class GeneratedClass {", "}" ); JavaFileObject expectedFile = JavaFileObjects.forSourceString("test.GeneratedClass", expected); -
ASSERT.about(javaSource()) .that(inputFile) .processedWith(exampleProcessors()) .compilesWithoutError() .and() .generatesSources(expectedFile); String input = Joiner.on('\n').join(
"package test;", "import com.example.Example;", "@Example", "public class ExampleClass {", "}" ); -
ASSERT.about(javaSource()) .that(inputFile) .processedWith(exampleProcessors()) .compilesWithoutError() .and() .generatesSources(expectedFile); String input = Joiner.on('\n').join(
"package test;", "import com.example.Example;", "@Example", "public class ExampleClass {", "}" ); final class TestProcessors { static Iterable<? extends Processor> exampleProcessors() { return Collections.singletonList( new ExampleProcessor() ); } } -
ASSERT.about(javaSource()) .that(inputFile) .processedWith(exampleProcessors()) .compilesWithoutError() .and() .generatesSources(expectedFile); String input = Joiner.on('\n').join(
"package test;", "import com.example.Example;", "@Example", "public class ExampleClass {", "}" ); final class TestProcessors { static Iterable<? extends Processor> exampleProcessors() { return Collections.singletonList( new ExampleProcessor() ); } } String expected = Joiner.on('\n').join( "package test;", "import com.example.Other;", "@Other", "public class GeneratedClass {", "}" ); -
ASSERT.about(javaSource()) .that(inputFile) .processedWith(exampleProcessors()) .compilesWithoutError() .and() .generatesSources(expectedFile); String input = Joiner.on('\n').join(
"package test;", "import com.example.Example;", "@Example", "public class ExampleClass {", "}" ); final class TestProcessors { static Iterable<? extends Processor> exampleProcessors() { return Collections.singletonList( new ExampleProcessor() ); } } String expected = Joiner.on('\n').join( "package test;", "import com.example.Other;", "@Other", "public class GeneratedClass {", "}" ); -
public class ExampleProcessor extends AbstractProcessor { // ... @Override
public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element } return false; } } Debugging -
public class ExampleProcessor extends AbstractProcessor { // ... @Override
public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { Set<? extends Element> elements = env.getElementsAnnotatedWith(Example.class); for (Element element : elements) { System.out.println(element } return false; } } Debugging