Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Annotation Processing Boilerplate Destruction (Droidcon NYC 2014)

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.

Jake Wharton

September 20, 2014
Tweet

More Decks by Jake Wharton

Other Decks in Technology

Transcript

  1. Annotation Processing
    Jake Wharton
    Boilerplate Destruction!

    View full-size slide

  2. Annotation Processing
    Jake Wharton
    Boilerplate Destruction!

    View full-size slide

  3. Dagger
    class CoffeeMaker {

    @Inject Lazy 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/

    View full-size slide

  4. Butter Knife

    View full-size slide

  5. 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/

    View full-size slide

  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/
    ButterKnife.inject(this);

    View full-size slide

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

    View full-size slide

  8. AutoParcel
    @AutoParcel

    abstract class SomeModel implements Parcelable {

    abstract String name();

    abstract List subModels();

    abstract Map modelsMap();


    static SomeModel create(String name,
    List subModels,
    Map modelsMap) {

    return new AutoParcel_SomeModel(name, subModels, modelsMap);

    }

    }
    github.com/frankiesardo/auto-parcel/

    View full-size slide

  9. 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";

    }

    View full-size slide

  10. 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";


    }

    View full-size slide

  11. Schematic
    @ContentProvider(authority = NotesProvider.AUTHORITY,

    database = NotesDatabase.class)

    public final class NotesProvider {

    // ...

    }
    github.com/SimonVT/schematic

    View full-size slide

  12. Doclet
    http://docs.oracle.com/javase/8/docs/technotes/guides/javadoc/doclet/overview.html

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  16. Java 1.5
    • JSR 14: Generics

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  19. Java 1.5
    • JSR 14: Generics
    • JSR 175: Annotations
    • Also a whole bunch of other really awesome things!
    • apt command-line tool

    View full-size slide

  20. Annotation Processing Tool: apt
    apt

    View full-size slide

  21. @Target(ElementType.TYPE)

    public @interface Example {

    }
    apt

    View full-size slide

  22. @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

    View full-size slide

  23. interface AnnotationProcessor {

    void process();

    }
    apt

    View full-size slide

  24. interface AnnotationProcessorFactory {

    AnnotationProcessor getProcessorFor(
    Set atds,
    AnnotationProcessorEnvironment env);

    Collection supportedAnnotationTypes();

    Collection supportedOptions();

    }
    interface AnnotationProcessor {

    void process();

    }
    apt

    View full-size slide

  25. apt
    public class ExampleAnnotationProcessorFactory
    implements AnnotationProcessorFactory {













    }

    View full-size slide

  26. apt
    public class ExampleAnnotationProcessorFactory
    implements AnnotationProcessorFactory {










    @Override public Collection supportedOptions() {

    return Collections.emptyList();

    }

    }

    View full-size slide

  27. apt
    public class ExampleAnnotationProcessorFactory
    implements AnnotationProcessorFactory {






    @Override public Collection supportedAnnotationTypes() {

    return Collections.singletonList(Example.class.getName());

    }


    @Override public Collection supportedOptions() {

    return Collections.emptyList();

    }

    }

    View full-size slide

  28. apt
    public class ExampleAnnotationProcessorFactory
    implements AnnotationProcessorFactory {

    @Override public AnnotationProcessor getProcessorFor(
    Set atds,

    AnnotationProcessorEnvironment env) {

    return new ExampleAnnotationProcessor(atds, env);

    }


    @Override public Collection supportedAnnotationTypes() {

    return Collections.singletonList(Example.class.getName());

    }


    @Override public Collection supportedOptions() {

    return Collections.emptyList();

    }

    }

    View full-size slide

  29. apt
    public class ExampleAnnotationProcessorFactory
    implements AnnotationProcessorFactory {

    @Override public AnnotationProcessor getProcessorFor(
    Set atds,

    AnnotationProcessorEnvironment env) {

    return new ExampleAnnotationProcessor(atds, env);

    }


    @Override public Collection supportedAnnotationTypes() {

    return Collections.singletonList(Example.class.getName());

    }


    @Override public Collection supportedOptions() {

    return Collections.emptyList();

    }

    }

    View full-size slide

  30. apt
    public class ExampleAnnotationProcessor
    implements AnnotationProcessor {









    }

    View full-size slide

  31. apt
    public class









    }
    public class ExampleAnnotationProcessor
    implements AnnotationProcessor {

    private final Set atds;

    private final AnnotationProcessorEnvironment env;


    public ExampleAnnotationProcessor(
    Set atds,

    AnnotationProcessorEnvironment env) {

    this.atds = atds;

    this.env = env;

    }

    }

    View full-size slide

  32. apt
    public class









    }
    public class


    Set atds

    }
    public class ExampleAnnotationProcessor
    implements AnnotationProcessor {









    }
    public class ExampleAnnotationProcessor
    implements AnnotationProcessor {

    // ...








    }
    public class ExampleAnnotationProcessor
    implements AnnotationProcessor {

    // ...


    @Override public void process() {

    !
    !

    }
    !

    }

    View full-size slide

  33. apt
    public class









    }
    public class


    Set 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);

    }

    }
    !
    !
    }

    View full-size slide

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

    View full-size slide

  35. JSR 269
    • 6 months after Java 5 released

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  38. JSR 269
    • 6 months after Java 5 released
    • “Pluggable Annotation Processing API”
    • Google, Oracle, Sun, BEA Systems sponsored
    • Simplify and standardize processing annotations

    View full-size slide

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

    View full-size slide

  40. interface AnnotationProcessor {

    void process();

    }

    View full-size slide

  41. interface Processor {

    Set getSupportedOptions();


    Set 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);

    }

    View full-size slide

  42. public class ExampleProcessor extends AbstractProcessor {














    }

    View full-size slide

  43. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {


    }











    }

    View full-size slide

  44. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }











    }

    View full-size slide

  45. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }


    @Override public Set getSupportedAnnotationTypes() {


    }







    }

    View full-size slide

  46. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }


    @Override public Set getSupportedAnnotationTypes() {

    return Collections.singleton(Example.class.getName());

    }







    }

    View full-size slide

  47. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }


    @Override public Set getSupportedAnnotationTypes() {

    return Collections.singleton(Example.class.getName());

    }


    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {


    }

    }

    View full-size slide

  48. public class ExampleProcessor extends AbstractProcessor {

    // ...

    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {

    !
    !
    !
    !
    !

    }

    }

    View full-size slide

  49. public class ExampleProcessor extends AbstractProcessor {

    // ...


    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {

    Set extends Element> elements

    = env.getElementsAnnotatedWith(Example.class);






    }

    }

    View full-size slide

  50. 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);

    }



    }

    }

    View full-size slide

  51. 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;

    }

    }

    View full-size slide

  52. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }


    @Override public Set 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;

    }

    }

    View full-size slide

  53. Execution
    • Happens automatically when javac runs.

    View full-size slide

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

    View full-size slide

  55. Service Loader

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  63. Execution
    • Happens automatically when javac runs.
    • ServiceLoader finds and creates Processor instances.
    • Inside a full JVM.
    • Compiled sources and its dependencies not available.

    View full-size slide

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

    View full-size slide

  65. Environment
    interface Processor {

    Set getSupportedOptions();


    Set 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);

    }

    View full-size slide

  66. Environment
    interface Processor {

    Set getSupportedOptions();


    Set 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);

    }

    View full-size slide

  67. Environment
    interface Processor {

    Set getSupportedOptions();


    Set 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);

    }

    View full-size slide

  68. Environment
    interface Processor {

    Set getSupportedOptions();


    Set 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);

    }

    View full-size slide

  69. Environment
    interface Processor {

    Set getSupportedOptions();


    Set 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);

    }

    View full-size slide

  70. Processing Environment

    View full-size slide

  71. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  72. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  73. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  74. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  75. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  76. 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);

    }

    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  83. 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);

    }

    }

    View full-size slide

  84. 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);

    }

    }

    View full-size slide

  85. 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);

    }

    }

    View full-size slide

  86. 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);

    }

    }

    View full-size slide

  87. 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);

    }

    }

    View full-size slide

  88. 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);

    }

    }

    View full-size slide

  89. 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;

    }

    }









    );




    View full-size slide

  90. 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;

    }

    }









    );




    View full-size slide

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




    View full-size slide

  92. 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;

    }

    }

    View full-size slide

  93. 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;

    }

    }

    View full-size slide

  94. 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;

    }

    }

    View full-size slide

  95. Round Environment

    View full-size slide

  96. 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);

    }

    View full-size slide

  97. 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);

    }

    View full-size slide

  98. Processing Rounds

    View full-size slide

  99. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }

    View full-size slide

  100. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

  101. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac

    View full-size slide

  102. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    javac

    View full-size slide

  103. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

  104. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Example

    public class RandomClass {

    // ...

    }
    javac
    ExampleProcessor

    View full-size slide

  105. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac
    ExampleProcessor

    View full-size slide

  106. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac
    OtherProcessor

    View full-size slide

  107. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

  108. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

  109. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }

    View full-size slide

  110. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

  111. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  115. 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 {

    // ...

    }

    View full-size slide

  116. 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 {

    // ...

    }

    View full-size slide

  117. 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 {

    // ...

    }

    View full-size slide

  118. 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 {

    // ...

    }

    View full-size slide

  119. 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 {

    // ...

    }

    View full-size slide

  120. 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 {

    // ...

    }

    View full-size slide

  121. 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 {

    // ...

    }

    View full-size slide

  122. 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 {

    // ...

    }

    View full-size slide

  123. 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 {

    // ...

    }

    View full-size slide

  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 {

    // ...

    }
    public class OtherGenClass {

    // ...

    }
    OtherGenClass.class
    public class OtherGenClass {

    // ...

    }

    View full-size slide

  125. JavaCompiler

    View full-size slide

  126. ExampleProcessor
    JavaCompiler
    OtherProcessor

    View full-size slide

  127. ProcessingEnvironment
    ExampleProcessor
    JavaCompiler
    Filer
    OtherProcessor

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  142. Parsing and Generation

    View full-size slide

  143. 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;

    }

    }

    View full-size slide

  144. 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'.





    View full-size slide

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




    View full-size slide

  146. Parsing and Generation

    View full-size slide

  147. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }


    View full-size slide

  148. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }




    List models = parseExampleAnnotations(env);

    View full-size slide

  149. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }




    List models = parseExampleAnnotations(env);










    List parseExampleAnnotations(RoundEnvironment env) {

    // ...

    }

    View full-size slide

  150. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }




    List models = parseExampleAnnotations(env);




    for (ExampleModel model : models) {

    emitExampleCode(model);

    }










    List parseExampleAnnotations(RoundEnvironment env) {

    // ...

    }

    View full-size slide

  151. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }




    List models = parseExampleAnnotations(env);




    for (ExampleModel model : models) {

    emitExampleCode(model);

    }










    List parseExampleAnnotations(RoundEnvironment env) {

    // ...

    }









    !
    !


    void emitExampleCode(ExampleModel model) {

    // ...

    }

    View full-size slide

  152. Parsing and Generation

    View full-size slide

  153. Parsing and Generation
    Element

    View full-size slide

  154. Parsing and Generation
    Element

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  160. Bring Your Own Dependencies

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  164. Bring Your Own Dependencies
    • Guava, Guava, Guava!
    • JavaWriter
    • google/auto ‘common’
    • AutoService (google/auto)

    View full-size slide

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

    View full-size slide

  166. 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();

    View full-size slide

  167. 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();

    View full-size slide

  168. 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);

    View full-size slide

  169. AutoService
    • Remember?

    META-­‐INF/services/javax.annotation.processing.Processor

    View full-size slide

  170. AutoService
    • Remember?

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

    View full-size slide

  171. AutoService
    • Remember?

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

    View full-size slide

  172. AutoService
    • Remember?

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

    View full-size slide

  173. Testing and Debugging

    View full-size slide

  174. Testing and Debugging
    • Google to the rescue!

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  179. final class TestProcessors {

    static Iterable extends Processor> exampleProcessors() {

    return Collections.singletonList(

    new ExampleProcessor()

    );

    }

    }

    View full-size slide

  180. String input = Joiner.on('\n').join(

    "package test;",

    "import com.example.Example;",

    "@Example",

    "public class ExampleClass {",

    "}"

    );

    View full-size slide

  181. String input = Joiner.on('\n').join(

    "package test;",

    "import com.example.Example;",

    "@Example",

    "public class ExampleClass {",

    "}"

    );
    JavaFileObject inputFile =

    JavaFileObjects.forSourceString("test.Test", input);

    View full-size slide

  182. JavaFileObject inputFile =

    JavaFileObjects.forSourceString("test.Test", input);
    String input = """\

    package test;

    import com.example.Example;

    @Example

    public class ExampleClass {

    }
    """
    .stripIndent()

    View full-size slide

  183. String expected = Joiner.on('\n').join(

    "package test;",

    "import com.example.Other;",

    "@Other",

    "public class GeneratedClass {",

    "}"

    );

    View full-size slide

  184. String expected = Joiner.on('\n').join(

    "package test;",

    "import com.example.Other;",

    "@Other",

    "public class GeneratedClass {",

    "}"

    );
    JavaFileObject expectedFile =

    JavaFileObjects.forSourceString("test.GeneratedClass", expected);

    View full-size slide

  185. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View full-size slide

  186. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View full-size slide

  187. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View full-size slide

  188. 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 {",

    "}"

    );

    View full-size slide

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

    );

    }

    }

    View full-size slide

  190. 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 {",

    "}"

    );

    View full-size slide

  191. 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 {",

    "}"

    );

    View full-size slide

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

    View full-size slide

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

    View full-size slide