Annotation Processing Boilerplate Destruction (Droidcon NYC 2014)

E68309f117985270285ade8082f4877d?s=47 Jake Wharton
September 20, 2014

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.

E68309f117985270285ade8082f4877d?s=128

Jake Wharton

September 20, 2014
Tweet

Transcript

  1. Annotation Processing Jake Wharton Boilerplate Destruction!

  2. Annotation Processing Jake Wharton Boilerplate Destruction!

  3. Dagger

  4. 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/
  5. Butter Knife

  6. 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/
  7. 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);
  8. AutoValue

  9. 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/
  10. AutoParcel

  11. 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/
  12. Schematic

  13. 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";
 }
  14. 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";
 
 }
  15. Schematic @ContentProvider(authority = NotesProvider.AUTHORITY,
 database = NotesDatabase.class)
 public final class

    NotesProvider {
 // ...
 } github.com/SimonVT/schematic
  16. Doclet http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/doclet/overview.html

  17. 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
  18. 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
  19. 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
  20. Java 1.5

  21. Java 1.5

  22. Java 1.5 • JSR 14: Generics

  23. Java 1.5 • JSR 14: Generics • JSR 175: Annotations

  24. Java 1.5 • JSR 14: Generics • JSR 175: Annotations

    • Also a whole bunch of other really awesome things!
  25. Java 1.5 • JSR 14: Generics • JSR 175: Annotations

    • Also a whole bunch of other really awesome things! • apt command-line tool
  26. Annotation Processing Tool: apt apt

  27. apt

  28. @Target(ElementType.TYPE)
 public @interface Example {
 } apt

  29. @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
  30. interface AnnotationProcessor {
 void process();
 } apt

  31. interface AnnotationProcessorFactory { 
 AnnotationProcessor getProcessorFor( Set<AnnotationTypeDeclaration> atds, AnnotationProcessorEnvironment env);

    
 Collection<String> supportedAnnotationTypes(); 
 Collection<String> supportedOptions(); 
 } interface AnnotationProcessor {
 void process();
 } apt
  32. apt public class ExampleAnnotationProcessorFactory implements AnnotationProcessorFactory { 
 
 


    
 
 
 
 
 
 
 
 
 
 }
  33. apt public class ExampleAnnotationProcessorFactory implements AnnotationProcessorFactory { 
 
 


    
 
 
 
 
 
 
 @Override public Collection<String> supportedOptions() {
 return Collections.emptyList();
 }
 }
  34. 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();
 }
 }
  35. 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();
 }
 }
  36. 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();
 }
 }
  37. apt public class ExampleAnnotationProcessor implements AnnotationProcessor { 
 
 


    
 
 
 
 
 
 }
  38. 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;
 } 
 }
  39. 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() {
 ! ! 
 } ! 
 }
  40. 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);
 }
 } ! ! }
  41. 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
  42. JSR 269

  43. JSR 269 • 6 months after Java 5 released

  44. JSR 269 • 6 months after Java 5 released •

    “Pluggable Annotation Processing API”
  45. JSR 269 • 6 months after Java 5 released •

    “Pluggable Annotation Processing API” • Google, Oracle, Sun, BEA Systems sponsored
  46. JSR 269 • 6 months after Java 5 released •

    “Pluggable Annotation Processing API” • Google, Oracle, Sun, BEA Systems sponsored • Simplify and standardize processing annotations
  47. 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
  48. interface AnnotationProcessor {
 void process();
 }

  49. 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);
 }
  50. public class ExampleProcessor extends AbstractProcessor {
 
 
 
 


    
 
 
 
 
 
 
 
 
 }
  51. public class ExampleProcessor extends AbstractProcessor {
 @Override public SourceVersion getSupportedSourceVersion()

    {
 
 }
 
 
 
 
 
 
 
 
 
 
 }
  52. public class ExampleProcessor extends AbstractProcessor {
 @Override public SourceVersion getSupportedSourceVersion()

    {
 return SourceVersion.latestSupported();
 }
 
 
 
 
 
 
 
 
 
 
 }
  53. public class ExampleProcessor extends AbstractProcessor {
 @Override public SourceVersion getSupportedSourceVersion()

    {
 return SourceVersion.latestSupported();
 }
 
 @Override public Set<String> getSupportedAnnotationTypes() {
 
 }
 
 
 
 
 
 
 }
  54. public class ExampleProcessor extends AbstractProcessor {
 @Override public SourceVersion getSupportedSourceVersion()

    {
 return SourceVersion.latestSupported();
 }
 
 @Override public Set<String> getSupportedAnnotationTypes() {
 return Collections.singleton(Example.class.getName());
 }
 
 
 
 
 
 
 }
  55. 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) {
 
 }
 }
  56. public class ExampleProcessor extends AbstractProcessor {
 // ...
 @Override public

    boolean process(
 Set<? extends TypeElement> annotations,
 RoundEnvironment env) {
 ! ! ! ! ! 
 }
 }
  57. public class ExampleProcessor extends AbstractProcessor {
 // ...
 
 @Override

    public boolean process(
 Set<? extends TypeElement> annotations,
 RoundEnvironment env) {
 Set<? extends Element> elements
 = env.getElementsAnnotatedWith(Example.class);
 
 
 
 
 
 }
 }
  58. 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);
 }
 
 
 }
 }
  59. 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;
 }
 }
  60. 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;
 }
 }
  61. Execution

  62. Execution • Happens automatically when javac runs.

  63. Execution • Happens automatically when javac runs. • ServiceLoader finds

    and creates Processor instances.
  64. Service Loader

  65. Service Loader • META-­‐INF/services/

  66. Service Loader • META-­‐INF/services/ • File name of implemented fully-qualified

    class name.
  67. Service Loader • META-­‐INF/services/ • File name of implemented fully-qualified

    class name. • javax.annotation.processing.Processor
  68. Service Loader • META-­‐INF/services/ • File name of implemented fully-qualified

    class name. • javax.annotation.processing.Processor • Each line implementation fully-qualified class name.
  69. 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
  70. Execution • Happens automatically when javac runs. • ServiceLoader finds

    and creates Processor instances.
  71. Execution • Happens automatically when javac runs. • ServiceLoader finds

    and creates Processor instances. • Inside a full JVM.
  72. Execution • Happens automatically when javac runs. • ServiceLoader finds

    and creates Processor instances. • Inside a full JVM. • Compiled sources and its dependencies not available.
  73. 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.
  74. Environment

  75. 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);
 }
  76. 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);
 }
  77. 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);
 }
  78. 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);
 }
  79. 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);
 }
  80. Processing Environment

  81. Processing Environment interface ProcessingEnvironment {
 Map<String, String> getOptions(); 
 SourceVersion

    getSourceVersion();
 
 Locale getLocale();
 
 Messager getMessager();
 
 Filer getFiler();
 
 Elements getElementUtils();
 
 Types getTypeUtils();
 }
  82. Processing Environment interface ProcessingEnvironment {
 Map<String, String> getOptions(); 
 SourceVersion

    getSourceVersion();
 
 Locale getLocale();
 
 Messager getMessager();
 
 Filer getFiler();
 
 Elements getElementUtils();
 
 Types getTypeUtils();
 }
  83. Processing Environment interface ProcessingEnvironment {
 Map<String, String> getOptions(); 
 SourceVersion

    getSourceVersion();
 
 Locale getLocale();
 
 Messager getMessager();
 
 Filer getFiler();
 
 Elements getElementUtils();
 
 Types getTypeUtils();
 }
  84. Processing Environment interface ProcessingEnvironment {
 Map<String, String> getOptions(); 
 SourceVersion

    getSourceVersion();
 
 Locale getLocale();
 
 Messager getMessager();
 
 Filer getFiler();
 
 Elements getElementUtils();
 
 Types getTypeUtils();
 }
  85. Processing Environment interface ProcessingEnvironment {
 Map<String, String> getOptions(); 
 SourceVersion

    getSourceVersion();
 
 Locale getLocale();
 
 Messager getMessager();
 
 Filer getFiler();
 
 Elements getElementUtils();
 
 Types getTypeUtils();
 }
  86. Elements

  87. 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);
 }
 }
  88. 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
  89. 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
  90. 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
  91. 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
  92. 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
  93. 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
  94. 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);
 }
 }
  95. 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);
 }
 }
  96. 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);
 }
 }
  97. 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);
 }
 }
  98. 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);
 }
 }
  99. 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);
 }
 }
  100. Environment

  101. 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;
 }
 } 
 
 
 
 
 
 
 
 
 );
 
 
 

  102. 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;
 }
 } 
 
 
 
 
 
 
 
 
 );
 
 
 

  103. 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()
 
 
 

  104. 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;
 }
 }
  105. 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;
 }
 }
  106. 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;
 }
 }
  107. Round Environment

  108. 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);
 }
  109. 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);
 }
  110. Processing Rounds

  111. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

  112. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    javac
  113. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac
  114. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac javac
  115. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Example
 public class RandomClass {
 // ...
 } javac
  116. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Example
 public class RandomClass {
 // ...
 } javac ExampleProcessor
  117. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other
 public class GeneratedClass {
 // ...
 } @Example
 public class RandomClass {
 // ...
 } javac ExampleProcessor
  118. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other
 public class GeneratedClass {
 // ...
 } @Example
 public class RandomClass {
 // ...
 } javac OtherProcessor
  119. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other
 public class GeneratedClass {
 // ...
 } @Example
 public class RandomClass {
 // ...
 } javac
  120. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other
 public class GeneratedClass {
 // ...
 } @Example
 public class RandomClass {
 // ...
 } javac
  121. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other
 public class GeneratedClass {
 // ...
 } @Example
 public class RandomClass {
 // ...
 }
  122. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other
 public class GeneratedClass {
 // ...
 } @Example
 public class RandomClass {
 // ...
 } javac
  123. Processing Rounds @Example
 public class RandomClass {
 // ...
 }

    ExampleProcessor OtherProcessor javac RandomClass.class out/ @Other
 public class GeneratedClass {
 // ...
 } @Example
 public class RandomClass {
 // ...
 } javac
  124. 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
  125. 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
  126. 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
  127. 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 {
 // ...
 }
  128. 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 {
 // ...
 }
  129. 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 {
 // ...
 }
  130. 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 {
 // ...
 }
  131. 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 {
 // ...
 }
  132. 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 {
 // ...
 }
  133. 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 {
 // ...
 }
  134. 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 {
 // ...
 }
  135. 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 {
 // ...
 }
  136. 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 {
 // ...
 }
  137. None
  138. JavaCompiler

  139. ExampleProcessor JavaCompiler OtherProcessor

  140. ProcessingEnvironment ExampleProcessor JavaCompiler Filer OtherProcessor

  141. ProcessingEnvironment ExampleProcessor JavaCompiler Filer OtherProcessor init() init()

  142. ProcessingEnvironment ExampleProcessor JavaCompiler Filer OtherProcessor init() init()

  143. ProcessingEnvironment ExampleProcessor JavaCompiler Filer OtherProcessor init() init()

  144. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() init()

  145. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init()

  146. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init()

  147. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init()

  148. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init()

  149. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init()

  150. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init()

  151. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init() process()

  152. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init() process()

  153. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init() process()

  154. ProcessingEnvironment ExampleProcessor JavaCompiler RoundEnvironment Filer OtherProcessor init() process() init() process()

  155. Parsing and Generation

  156. 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;
 }
 }
  157. 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'.
 
 
 
 

  158. 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.
 
 
 

  159. Parsing and Generation

  160. Parsing and Generation @Override public boolean process(
 Set<? extends TypeElement>

    annotations,
 RoundEnvironment env) {
 
 
 
 
 return false;
 }

  161. Parsing and Generation @Override public boolean process(
 Set<? extends TypeElement>

    annotations,
 RoundEnvironment env) {
 
 
 
 
 return false;
 }
 
 
 
 List<ExampleModel> models = parseExampleAnnotations(env);
  162. 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) {
 // ...
 }
  163. 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) {
 // ...
 }
  164. 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) {
 // ...
 }
  165. Parsing and Generation

  166. Parsing and Generation Element

  167. Parsing and Generation Element

  168. Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”

    public: true
  169. Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”

    public: true
  170. Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”

    public: true ExampleModel ! name: “Foo” package: “com.example” public: true element:
  171. Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”

    public: true ExampleModel ! name: “Foo” package: “com.example” public: true element: GeneratedClass
  172. Parsing and Generation Element ExampleModel ! name: “Foo” package: “com.example”

    public: true ExampleModel ! name: “Foo” package: “com.example” public: true element: GeneratedClass
  173. Bring Your Own Dependencies

  174. Bring Your Own Dependencies • Guava, Guava, Guava!

  175. Bring Your Own Dependencies • Guava, Guava, Guava! • JavaWriter

  176. Bring Your Own Dependencies • Guava, Guava, Guava! • JavaWriter

    • google/auto ‘common’
  177. Bring Your Own Dependencies • Guava, Guava, Guava! • JavaWriter

    • google/auto ‘common’ • AutoService (google/auto)
  178. JavaWriter

  179. JavaWriter JavaFileObject sourceFile = filer.createSourceFile(name, element);

  180. 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();
  181. 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();
  182. 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);
  183. AutoService

  184. AutoService • Remember?
 META-­‐INF/services/javax.annotation.processing.Processor

  185. AutoService • Remember?
 META-­‐INF/services/javax.annotation.processing.Processor • AutoService generates service files from

    annotations
  186. AutoService • Remember?
 META-­‐INF/services/javax.annotation.processing.Processor • AutoService generates service files from

    annotations ! public class ExampleProcessor extends AbstractProcessor { // ... }
  187. AutoService • Remember?
 META-­‐INF/services/javax.annotation.processing.Processor • AutoService generates service files from

    annotations ! public class ExampleProcessor extends AbstractProcessor { // ... } @AutoService(Processor.class)
  188. Testing and Debugging

  189. Testing and Debugging • Google to the rescue!

  190. Testing and Debugging • Google to the rescue! • Guava

  191. Testing and Debugging • Google to the rescue! • Guava

    • Guava
  192. Testing and Debugging • Google to the rescue! • Guava

    • Guava • compile-testing
  193. Testing and Debugging • Google to the rescue! • Guava

    • Guava • compile-testing • Truth
  194. final class TestProcessors {
 static Iterable<? extends Processor> exampleProcessors() {


    return Collections.singletonList(
 new ExampleProcessor()
 );
 }
 }
  195. String input = Joiner.on('\n').join(
 "package test;",
 "import com.example.Example;",
 "@Example",
 "public

    class ExampleClass {",
 "}"
 );
  196. String input = Joiner.on('\n').join(
 "package test;",
 "import com.example.Example;",
 "@Example",
 "public

    class ExampleClass {",
 "}"
 ); JavaFileObject inputFile =
 JavaFileObjects.forSourceString("test.Test", input);
  197. JavaFileObject inputFile =
 JavaFileObjects.forSourceString("test.Test", input); String input = """\
 package

    test; 
 import com.example.Example; 
 @Example
 public class ExampleClass {
 } """ .stripIndent()
  198. String expected = Joiner.on('\n').join(
 "package test;",
 "import com.example.Other;",
 "@Other",
 "public

    class GeneratedClass {",
 "}"
 );
  199. String expected = Joiner.on('\n').join(
 "package test;",
 "import com.example.Other;",
 "@Other",
 "public

    class GeneratedClass {",
 "}"
 ); JavaFileObject expectedFile =
 JavaFileObjects.forSourceString("test.GeneratedClass", expected);
  200. ASSERT.about(javaSource())
 .that(inputFile)
 .processedWith(exampleProcessors())
 .compilesWithoutError()
 .and()
 .generatesSources(expectedFile);

  201. ASSERT.about(javaSource())
 .that(inputFile)
 .processedWith(exampleProcessors())
 .compilesWithoutError()
 .and()
 .generatesSources(expectedFile);

  202. ASSERT.about(javaSource())
 .that(inputFile)
 .processedWith(exampleProcessors())
 .compilesWithoutError()
 .and()
 .generatesSources(expectedFile);

  203. 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 {",
 "}"
 );
  204. 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()
 );
 }
 }
  205. 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 {",
 "}"
 );
  206. 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 {",
 "}"
 );
  207. Debugging

  208. 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
  209. 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
  210. Now What?

  211. None
  212. Questions?