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

Annotation Processing Boilerplate Destruction (Square Waterloo 2014)

Annotation Processing Boilerplate Destruction (Square Waterloo 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 cover on showcasing existing annotation processors for Android development. We will then cover how to get started writing your own annotation processor.

Video: http://youtu.be/dOcs-NKK-RA

Jake Wharton
PRO

September 23, 2014
Tweet

More Decks by Jake Wharton

Other Decks in Technology

Transcript

  1. Annotation Processing
    Jake Wharton
    Boilerplate Destruction!

    View Slide

  2. Annotation Processing
    Jake Wharton
    Boilerplate Destruction!

    View Slide

  3. JSR 269

    View Slide

  4. JSR 269
    • 6 months after Java 5 released

    View Slide

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

    View Slide

  6. JSR 269
    • 6 months after Java 5 released
    • “Pluggable Annotation Processing API”
    • Preceded by doclet and apt standalone tools

    View Slide

  7. JSR 269
    • 6 months after Java 5 released
    • “Pluggable Annotation Processing API”
    • Preceded by doclet and apt standalone tools
    • Google, Oracle, Sun, BEA Systems sponsored

    View Slide

  8. JSR 269
    • 6 months after Java 5 released
    • “Pluggable Annotation Processing API”
    • Preceded by doclet and apt standalone tools
    • Google, Oracle, Sun, BEA Systems sponsored
    • Simplify and standardize processing annotations

    View Slide

  9. JSR 269
    • 6 months after Java 5 released
    • “Pluggable Annotation Processing API”
    • Preceded by doclet and apt standalone tools
    • Google, Oracle, Sun, BEA Systems sponsored
    • Simplify and standardize processing annotations
    • No standalone tool, part of javac

    View Slide

  10. AutoValue
    @AutoValue

    abstract 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 Slide

  11. AutoValue github.com/google/auto/
    final class AutoValue_Animal extends Animal {

    private final String name;

    private final int numberOfLegs;


    AutoValue_Animal(

    String name,

    int numberOfLegs) {

    if (name == null) {

    throw new NullPointerException("Null name");

    }

    this.name = name;

    this.numberOfLegs = numberOfLegs;

    }


    @Override

    String name() {

    return name;

    }


    @Override

    int numberOfLegs() {

    return numberOfLegs;

    }


    @Override

    public String toString() {

    return "Animal{"

    + "name=" + name

    + ", numberOfLegs=" + numberOfLegs

    + "}";

    }


    @Override

    public boolean equals(Object o) {

    if (o == this) {

    return true;

    }

    if (o instanceof Animal) {

    Animal that = (Animal) o;

    return (this.name.equals(that.name()))

    && (this.numberOfLegs == that.numberOfLegs());

    }

    return false;

    }


    @Override

    public int hashCode() {

    int h = 1;

    h *= 1000003;

    h ^= name.hashCode();

    h *= 1000003;

    h ^= numberOfLegs;

    return h;

    }

    }

    View Slide

  12. AutoValue github.com/google/auto/
    final class AutoValue_Animal extends Animal {

    private final String name;

    private final int numberOfLegs;


    AutoValue_Animal(

    String name,

    int numberOfLegs) {

    if (name == null) {

    throw new NullPointerException("Null name");

    }

    this.name = name;

    this.numberOfLegs = numberOfLegs;

    }


    @Override

    String name() {

    return name;

    }


    @Override

    int numberOfLegs() {

    return numberOfLegs;

    }


    @Override

    public String toString() {

    return "Animal{"

    + "name=" + name

    + ", numberOfLegs=" + numberOfLegs

    + "}";

    }


    @Override

    public boolean equals(Object o) {

    if (o == this) {

    return true;

    }

    if (o instanceof Animal) {

    Animal that = (Animal) o;

    return (this.name.equals(that.name()))

    && (this.numberOfLegs == that.numberOfLegs());

    }

    return false;

    }


    @Override

    public int hashCode() {

    int h = 1;

    h *= 1000003;

    h ^= name.hashCode();

    h *= 1000003;

    h ^= numberOfLegs;

    return h;

    }

    }
    Final fields
    Constructor
    Getters
    .toString()
    .equals()
    .hashCode()

    View Slide

  13. AutoParcel github.com/frankiesardo/auto-parcel/
    @AutoParcel

    abstract class Animal implements Parcelable {

    static Animal create(String name, int numberOfLegs) {

    return new AutoParcel_Animal(name, numberOfLegs);

    }

    abstract String name();

    abstract int numberOfLegs();

    }

    View Slide

  14. AutoParcel github.com/frankiesardo/auto-parcel/
    @AutoParcel

    abstract class Animal implements Parcelable {

    static Animal create(String name, int numberOfLegs) {

    return new AutoParcel_Animal(name, numberOfLegs);

    }

    abstract String name();

    abstract int numberOfLegs();

    }

    View Slide

  15. AutoParcel
    final class AutoParcel_Animal extends Animal {

    private final String name;

    private final int numberOfLegs;


    AutoParcel_Animal(

    String name,

    int numberOfLegs) {

    if (name == null) {

    throw new NullPointerException("Null name");

    }

    this.name = name;

    this.numberOfLegs = numberOfLegs;

    }


    @Override

    String name() {

    return name;

    }


    @Override

    int numberOfLegs() {

    return numberOfLegs;

    }


    @Override

    public String toString() {

    return "Animal{"

    + "name=" + name

    + ", numberOfLegs=" + numberOfLegs

    + "}";

    }


    @Override

    public boolean equals(Object o) {

    if (o == this) {

    return true;

    }

    if (o instanceof Animal) {

    Animal that = (Animal) o;

    return (this.name.equals(that.name()))

    && (this.numberOfLegs == that.numberOfLegs());

    }

    return false;

    }


    @Override

    public int hashCode() {

    int h = 1;

    h *= 1000003;

    h ^= name.hashCode();

    h *= 1000003;

    h ^= numberOfLegs;

    return h;

    }


    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

    @Override public Animal createFromParcel(Parcel in) {

    return new AutoParcel_Animal(in);

    }

    @Override public Animal[] newArray(int size) {

    return new Animal[size];

    }

    };


    private final static ClassLoader CL = AutoParcel_Animal.class.getClassLoader();


    private AutoParcel_Animal(Parcel in) {

    this(

    (String) in.readValue(CL),

    (Integer) in.readValue(CL));

    }


    @Override public void writeToParcel(Parcel dest, int flags) {

    dest.writeValue(name);

    dest.writeValue(numberOfLegs);

    }


    @Override public int describeContents() {

    return 0;

    }

    }
    github.com/frankiesardo/auto-parcel/

    View Slide

  16. AutoParcel
    final class AutoParcel_Animal extends Animal {

    private final String name;

    private final int numberOfLegs;


    AutoParcel_Animal(

    String name,

    int numberOfLegs) {

    if (name == null) {

    throw new NullPointerException("Null name");

    }

    this.name = name;

    this.numberOfLegs = numberOfLegs;

    }


    @Override

    String name() {

    return name;

    }


    @Override

    int numberOfLegs() {

    return numberOfLegs;

    }


    @Override

    public String toString() {

    return "Animal{"

    + "name=" + name

    + ", numberOfLegs=" + numberOfLegs

    + "}";

    }


    @Override

    public boolean equals(Object o) {

    if (o == this) {

    return true;

    }

    if (o instanceof Animal) {

    Animal that = (Animal) o;

    return (this.name.equals(that.name()))

    && (this.numberOfLegs == that.numberOfLegs());

    }

    return false;

    }


    @Override

    public int hashCode() {

    int h = 1;

    h *= 1000003;

    h ^= name.hashCode();

    h *= 1000003;

    h ^= numberOfLegs;

    return h;

    }


    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

    @Override public Animal createFromParcel(Parcel in) {

    return new AutoParcel_Animal(in);

    }

    @Override public Animal[] newArray(int size) {

    return new Animal[size];

    }

    };


    private final static ClassLoader CL = AutoParcel_Animal.class.getClassLoader();


    private AutoParcel_Animal(Parcel in) {

    this(

    (String) in.readValue(CL),

    (Integer) in.readValue(CL));

    }


    @Override public void writeToParcel(Parcel dest, int flags) {

    dest.writeValue(name);

    dest.writeValue(numberOfLegs);

    }


    @Override public int describeContents() {

    return 0;

    }

    }
    Final fields
    Constructor
    Getters
    .toString()
    .equals()
    .hashCode()
    github.com/frankiesardo/auto-parcel/
    CREATOR
    Parcel constructor
    Parcel writer

    View Slide

  17. Butter Knife
    class SimpleActivity extends Activity {

    @InjectView(R.id.title)
    TextView title;

    @InjectView(R.id.hello)
    Button hello;

    @InjectView(R.id.list_of_things)
    ListView listOfThings;

    @OnClick(R.id.hello)
    void sayHello() {

    Toast.makeText(this, "Hello, views!", LENGTH_SHORT).show();

    }
    !

    // ...

    }
    jakewharton.github.io/butterknife/

    View Slide

  18. Butter Knife jakewharton.github.io/butterknife/
    static void inject(Finder finder, final SimpleActivity target,
    Object source) {

    View view;

    view = finder.findRequired(source, 2130968576, "field 'title'");

    target.title = (android.widget.TextView) view;

    view = finder.findRequired(source, 2130968578,
    "field 'hello' and method 'sayHello'");

    target.hello = (android.widget.Button) view;

    view.setOnClickListener(new View.OnClickListener() {

    @Override public void onClick(View p0) {

    target.sayHello();

    }

    });

    view = finder.findRequired(source, 2130968579, "field 'listOfThings'");
    target.listOfThings = (android.widget.ListView) view;

    }

    View Slide

  19. Butter Knife jakewharton.github.io/butterknife/
    static void inject(Finder finder, final SimpleActivity target,
    Object source) {

    View view;

    view = finder.findRequired(source, 2130968576, "field 'title'");

    target.title = (android.widget.TextView) view;

    view = finder.findRequired(source, 2130968578,
    "field 'hello' and method 'sayHello'");

    target.hello = (android.widget.Button) view;

    view.setOnClickListener(new View.OnClickListener() {

    @Override public void onClick(View p0) {

    target.sayHello();

    }

    });

    view = finder.findRequired(source, 2130968579, "field 'listOfThings'");
    target.listOfThings = (android.widget.ListView) view;

    }

    View Slide

  20. Butter Knife jakewharton.github.io/butterknife/
    static void inject(Finder finder, final SimpleActivity target,
    Object source) {

    View view;

    view = finder.findRequired(source, 2130968576, "field 'title'");

    target.title = (android.widget.TextView) view;

    view = finder.findRequired(source, 2130968578,
    "field 'hello' and method 'sayHello'");

    target.hello = (android.widget.Button) view;

    view.setOnClickListener(new View.OnClickListener() {

    @Override public void onClick(View p0) {

    target.sayHello();

    }

    });

    view = finder.findRequired(source, 2130968579, "field 'listOfThings'");
    target.listOfThings = (android.widget.ListView) view;

    }

    View Slide

  21. Butter Knife jakewharton.github.io/butterknife/
    static void inject(Finder finder, final SimpleActivity target,
    Object source) {

    View view;

    view = finder.findRequired(source, 2130968576, "field 'title'");

    target.title = (android.widget.TextView) view;

    view = finder.findRequired(source, 2130968578,
    "field 'hello' and method 'sayHello'");

    target.hello = (android.widget.Button) view;

    view.setOnClickListener(new View.OnClickListener() {

    @Override public void onClick(View p0) {

    target.sayHello();

    }

    });

    view = finder.findRequired(source, 2130968579, "field 'listOfThings'");
    target.listOfThings = (android.widget.ListView) view;

    }

    View Slide

  22. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }


    View Slide

  23. 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 Slide

  24. Dagger square.github.io/dagger/
    class DripCoffeeModule$$ModuleAdapter
    extends ModuleAdapter {

    @Override

    public void getBindings(BindingsGroup bindings,
    DripCoffeeModule module) {

    bindings.contributeProvidesBinding("coffee.Heater",
    new ProvideHeaterAdapter(module));
    bindings.contributeProvidesBinding("coffee.Pump",

    new ProvidePumpAdapter(module));

    }
    }

    View Slide

  25. Dagger square.github.io/dagger/
    class DripCoffeeModule$$ModuleAdapter
    extends ModuleAdapter {

    @Override

    public void getBindings(BindingsGroup bindings,
    DripCoffeeModule module) {

    bindings.contributeProvidesBinding("coffee.Heater",
    new ProvideHeaterAdapter(module));
    bindings.contributeProvidesBinding("coffee.Pump",

    new ProvidePumpAdapter(module));

    }
    }

    View Slide

  26. Dagger square.github.io/dagger/
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }

    View Slide

  27. Dagger square.github.io/dagger/
    class CoffeeMaker$$InjectAdapter

    extends Binding {


    private Binding> heater;

    private Binding pump;


    CoffeeMaker$$InjectAdapter() {

    super("coffee.CoffeeMaker", CoffeeMaker.class);

    }


    @Override public void attach(Linker linker) {

    heater = linker.requestBinding("dagger.Lazy");

    pump = linker.requestBinding("coffee.Pump");

    }
    !
    // ...

    }

    View Slide

  28. Dagger square.github.io/dagger/
    class CoffeeMaker$$InjectAdapter

    extends Binding {


    private Binding> heater;

    private Binding pump;


    CoffeeMaker$$InjectAdapter() {

    super("coffee.CoffeeMaker", CoffeeMaker.class);

    }


    @Override public void attach(Linker linker) {

    heater = linker.requestBinding("dagger.Lazy");

    pump = linker.requestBinding("coffee.Pump");

    }
    !
    // ...

    }

    View Slide

  29. Dagger square.github.io/dagger/
    class CoffeeMaker$$InjectAdapter

    extends Binding {


    // ...


    @Override public CoffeeMaker get() {

    CoffeeMaker result = new CoffeeMaker();

    injectMembers(result);

    return result;

    }


    @Override public void injectMembers(CoffeeMaker object) {

    object.heater = heater.get();

    object.pump = pump.get();

    }

    }

    View Slide

  30. Dagger square.github.io/dagger/
    class CoffeeMaker$$InjectAdapter

    extends Binding {


    // ...


    @Override public CoffeeMaker get() {

    CoffeeMaker result = new CoffeeMaker();

    injectMembers(result);

    return result;

    }


    @Override public void injectMembers(CoffeeMaker object) {

    object.heater = heater.get();

    object.pump = pump.get();

    }

    }

    View Slide

  31. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }
    class CoffeeMaker {

    @Inject Lazy heater;

    @Inject Pump pump;

    // ...

    }

    View Slide

  32. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }
    class CoffeeMaker {

    @Inject Lazy heater;

    @Inject Pump pump;

    // ...

    }
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }

    View Slide

  33. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }
    class CoffeeMaker {

    @Inject Lazy heater;

    @Inject Pump pump;

    // ...

    }
    class DripCoffeeModule$$ModuleAdapter
    extends ModuleAdapter {

    @Override

    public void getBindings(BindingsGroup bindings,
    DripCoffeeModule module) {

    bindings.contributeProvidesBinding("coffee.Heater",
    new ProvideHeaterAdapter(module));
    bindings.contributeProvidesBinding("coffee.Pump",

    new ProvidePumpAdapter(module));

    }
    }
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }

    View Slide

  34. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }
    class CoffeeMaker {

    @Inject Lazy heater;

    @Inject Pump pump;

    // ...

    }
    class DripCoffeeModule$$ModuleAdapter
    extends ModuleAdapter {

    @Override

    public void getBindings(BindingsGroup bindings,
    DripCoffeeModule module) {

    bindings.contributeProvidesBinding("coffee.Heater",
    new ProvideHeaterAdapter(module));
    bindings.contributeProvidesBinding("coffee.Pump",

    new ProvidePumpAdapter(module));

    }
    }
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }
    class ObjectGraph {

    // ...

    }

    View Slide

  35. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }
    class CoffeeMaker {

    @Inject Lazy heater;

    @Inject Pump pump;

    // ...

    }
    class DripCoffeeModule$$ModuleAdapter
    extends ModuleAdapter {

    @Override

    public void getBindings(BindingsGroup bindings,
    DripCoffeeModule module) {

    bindings.contributeProvidesBinding("coffee.Heater",
    new ProvideHeaterAdapter(module));
    bindings.contributeProvidesBinding("coffee.Pump",

    new ProvidePumpAdapter(module));

    }
    }
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }
    class CoffeeMaker$$InjectAdapter

    extends Binding {


    @Override public void attach(Linker linker) {

    heater = linker.requestBinding("dagger.Lazy");

    pump = linker.requestBinding("coffee.Pump");

    }

    @Override public void injectMembers(CoffeeMaker object) {

    object.heater = heater.get();

    object.pump = pump.get();

    }
    !
    // ...

    }
    class ObjectGraph {

    // ...

    }

    View Slide

  36. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }
    class CoffeeMaker {

    @Inject Lazy heater;

    @Inject Pump pump;

    // ...

    }
    class DripCoffeeModule$$ModuleAdapter
    extends ModuleAdapter {

    @Override

    public void getBindings(BindingsGroup bindings,
    DripCoffeeModule module) {

    bindings.contributeProvidesBinding("coffee.Heater",
    new ProvideHeaterAdapter(module));
    bindings.contributeProvidesBinding("coffee.Pump",

    new ProvidePumpAdapter(module));

    }
    }
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }
    class CoffeeMaker$$InjectAdapter

    extends Binding {


    @Override public void attach(Linker linker) {

    heater = linker.requestBinding("dagger.Lazy");

    pump = linker.requestBinding("coffee.Pump");

    }

    @Override public void injectMembers(CoffeeMaker object) {

    object.heater = heater.get();

    object.pump = pump.get();

    }
    !
    // ...

    }
    class ObjectGraph {

    // ...

    }

    View Slide

  37. Dagger square.github.io/dagger/
    @Module(injects = CoffeeApp.class)

    class DripCoffeeModule {

    @Provides @Singleton Heater provideHeater() {

    return new ElectricHeater();

    }


    @Provides Pump providePump(Thermosiphon pump) {

    return pump;

    }

    }
    class CoffeeMaker {

    @Inject Lazy heater;

    @Inject Pump pump;

    // ...

    }
    class DripCoffeeModule$$ModuleAdapter
    extends ModuleAdapter {

    @Override

    public void getBindings(BindingsGroup bindings,
    DripCoffeeModule module) {

    bindings.contributeProvidesBinding("coffee.Heater",
    new ProvideHeaterAdapter(module));
    bindings.contributeProvidesBinding("coffee.Pump",

    new ProvidePumpAdapter(module));

    }
    }
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }
    class CoffeeMaker$$InjectAdapter

    extends Binding {


    @Override public void attach(Linker linker) {

    heater = linker.requestBinding("dagger.Lazy");

    pump = linker.requestBinding("coffee.Pump");

    }

    @Override public void injectMembers(CoffeeMaker object) {

    object.heater = heater.get();

    object.pump = pump.get();

    }
    !
    // ...

    }

    View Slide

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

    }
    github.com/SimonVT/schematic/

    View Slide

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


    }
    github.com/SimonVT/schematic/

    View Slide

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

    database = NotesDatabase.class)

    public final class NotesProvider {

    // ...

    }
    github.com/SimonVT/schematic/

    View Slide

  41. Execution

    View Slide

  42. Execution
    • Happens automatically when javac runs.

    View Slide

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

    View Slide

  44. 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 Slide

  45. public class ExampleProcessor extends AbstractProcessor {














    }

    View Slide

  46. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {


    }











    }

    View Slide

  47. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }











    }

    View Slide

  48. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }


    @Override public Set getSupportedAnnotationTypes() {


    }







    }

    View Slide

  49. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }


    @Override public Set getSupportedAnnotationTypes() {

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

    }







    }

    View Slide

  50. 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 Slide

  51. public class ExampleProcessor extends AbstractProcessor {

    // ...

    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {

    !
    !
    !
    !
    !

    }

    }

    View Slide

  52. public class ExampleProcessor extends AbstractProcessor {

    // ...


    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {

    Set extends Element> elements

    = env.getElementsAnnotatedWith(Example.class);






    }

    }

    View Slide

  53. 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 Slide

  54. 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 Slide

  55. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  59. 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 Slide

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

  61. 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 Slide

  62. 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 Slide

  63. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View Slide

  64. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View Slide

  65. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View Slide

  66. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View Slide

  67. 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 Slide

  68. 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 Slide

  69. 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 Slide

  70. 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 Slide

  71. 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 Slide

  72. 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 Slide

  73. 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 Slide

  74. 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 Slide

  75. 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 Slide

  76. 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 Slide

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

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

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

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

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

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

  83. 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 Slide

  84. 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 Slide

  85. 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 Slide

  86. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }

    View Slide

  87. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View Slide

  88. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac

    View Slide

  89. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    javac

    View Slide

  90. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View Slide

  91. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Example

    public class RandomClass {

    // ...

    }
    javac
    ExampleProcessor

    View Slide

  92. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac
    ExampleProcessor

    View Slide

  93. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac
    OtherProcessor

    View Slide

  94. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View Slide

  95. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View Slide

  96. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }

    View Slide

  97. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View Slide

  98. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View Slide

  99. 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 Slide

  100. 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 Slide

  101. 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 Slide

  102. 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 Slide

  103. 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 Slide

  104. 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 Slide

  105. 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 Slide

  106. 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 Slide

  107. 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 Slide

  108. 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 Slide

  109. 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 Slide

  110. 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 Slide

  111. 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 Slide

  112. View Slide

  113. JavaCompiler

    View Slide

  114. ExampleProcessor
    JavaCompiler
    OtherProcessor

    View Slide

  115. ProcessingEnvironment
    ExampleProcessor
    JavaCompiler
    Filer
    OtherProcessor

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  130. 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 Slide

  131. 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 Slide

  132. 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 Slide

  133. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }


    View Slide

  134. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }




    List models = parseExampleAnnotations(env);

    View Slide

  135. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }




    List models = parseExampleAnnotations(env);










    List parseExampleAnnotations(RoundEnvironment env) {

    // ...

    }

    View Slide

  136. 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 Slide

  137. 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 Slide

  138. Parsing and Generation
    Element

    View Slide

  139. Parsing and Generation
    Element

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  145. Bring Your Own Dependencies

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  150. JavaWriter

    View Slide

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

    View Slide

  152. 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 Slide

  153. 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 Slide

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

    View Slide

  155. AutoService

    View Slide

  156. AutoService
    • Remember “ServiceLoader finds and creates”

    View Slide

  157. AutoService
    • Remember “ServiceLoader finds and creates”
    • AutoService generates service files from annotations

    View Slide

  158. AutoService
    • Remember “ServiceLoader finds and creates”
    • AutoService generates service files from annotations
    !
    public class ExampleProcessor extends AbstractProcessor {
    // ...
    }

    View Slide

  159. AutoService
    • Remember “ServiceLoader finds and creates”
    • AutoService generates service files from annotations
    !
    public class ExampleProcessor extends AbstractProcessor {
    // ...
    }
    @AutoService(Processor.class)

    View Slide

  160. Testing and Debugging

    View Slide

  161. Testing and Debugging
    • Google to the rescue!

    View Slide

  162. Testing and Debugging
    • Google to the rescue!
    • Guava, Guava, Guava!

    View Slide

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

    View Slide

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

    View Slide

  165. final class TestProcessors {

    static Iterable extends Processor> exampleProcessors() {

    return Collections.singletonList(

    new ExampleProcessor()

    );

    }

    }

    View Slide

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

    "package test;",

    "import com.example.Example;",

    "@Example",

    "public class ExampleClass {",

    "}"

    );

    View Slide

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

    "package test;",

    "import com.example.Example;",

    "@Example",

    "public class ExampleClass {",

    "}"

    );
    JavaFileObject inputFile =

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

    View Slide

  168. JavaFileObject inputFile =

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

    package test;

    import com.example.Example;

    @Example

    public class ExampleClass {

    }
    """
    .stripIndent()

    View Slide

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

    "package test;",

    "import com.example.Other;",

    "@Other",

    "public class GeneratedClass {",

    "}"

    );

    View Slide

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

    "package test;",

    "import com.example.Other;",

    "@Other",

    "public class GeneratedClass {",

    "}"

    );
    JavaFileObject expectedFile =

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

    View Slide

  171. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View Slide

  172. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View Slide

  173. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View Slide

  174. 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 Slide

  175. 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 Slide

  176. 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 Slide

  177. 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 Slide

  178. 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 Slide

  179. 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 Slide

  180. Now What?

    View Slide

  181. View Slide

  182. Questions?

    View Slide