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

Annotation Processing Boilerplate Destruction (Square Waterloo 2014)

Jake Wharton
September 23, 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

September 23, 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. JSR 269
    • 6 months after Java 5 released

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  6. 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 full-size 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
    • Simplify and standardize processing annotations

    View full-size 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
    • No standalone tool, part of javac

    View full-size slide

  9. 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 full-size slide

  10. 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 full-size 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;

    }

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

    View full-size slide

  12. 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 full-size 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 full-size slide

  14. 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 full-size 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;

    }

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

    View full-size slide

  16. 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 full-size slide

  17. 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 full-size 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 full-size 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 full-size 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 full-size slide

  21. 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 full-size slide

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

  23. 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 full-size 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 full-size slide

  25. 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 full-size slide

  26. 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 full-size 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 full-size slide

  28. 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 full-size 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 full-size slide

  30. 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 full-size 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;

    // ...

    }
    class ProvideHeaterProvidesAdapter
    extends ProvidesBinding {

    private final DripCoffeeModule module;


    public ProvideHeaterProvidesAdapter(DripCoffeeModule module) {

    this.module = module;

    }


    @Override

    public Heater get() {

    return module.provideHeater();

    }

    }

    View full-size 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 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 full-size 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();

    }

    }
    class ObjectGraph {

    // ...

    }

    View full-size 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 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 full-size 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 full-size 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();

    }
    !
    // ...

    }

    View full-size slide

  37. 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 full-size slide

  38. 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 full-size slide

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

    database = NotesDatabase.class)

    public final class NotesProvider {

    // ...

    }
    github.com/SimonVT/schematic/

    View full-size slide

  40. Execution
    • Happens automatically when javac runs.

    View full-size slide

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

    View full-size slide

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

  43. public class ExampleProcessor extends AbstractProcessor {














    }

    View full-size slide

  44. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {


    }











    }

    View full-size slide

  45. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }











    }

    View full-size slide

  46. public class ExampleProcessor extends AbstractProcessor {

    @Override public SourceVersion getSupportedSourceVersion() {

    return SourceVersion.latestSupported();

    }


    @Override public Set getSupportedAnnotationTypes() {


    }







    }

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

    }







    }

    View full-size slide

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

  49. public class ExampleProcessor extends AbstractProcessor {

    // ...

    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {

    !
    !
    !
    !
    !

    }

    }

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






    }

    }

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

    }



    }

    }

    View full-size 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);

    for (Element element : elements) {

    System.out.println(element);

    }


    return false;

    }

    }

    View full-size slide

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

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

    View full-size slide

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

    View full-size slide

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

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

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

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

  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 full-size slide

  61. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  62. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  63. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

  64. Processing Environment
    interface ProcessingEnvironment {

    Map getOptions();

    SourceVersion getSourceVersion();


    Locale getLocale();


    Messager getMessager();


    Filer getFiler();


    Elements getElementUtils();


    Types getTypeUtils();

    }

    View full-size slide

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

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

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

    }

    }
    TypeElement
    VariableElement

    View full-size 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
    VariableElement
    ExecutableElement

    View full-size 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
    ExecutableElement
    ExecutableElement

    View full-size 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
    ExecutableElement

    View full-size 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 full-size slide

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

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

  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 full-size 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 full-size 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 full-size 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 full-size slide

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

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

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

    }

    return false;

    }

    }









    element.asType()




    View full-size 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.asType());

    }

    return false;

    }

    }

    View full-size 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(element.asType());

    }

    return false;

    }

    }

    View full-size 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 full-size slide

  84. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }

    View full-size slide

  85. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

  86. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac

    View full-size slide

  87. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    javac

    View full-size slide

  88. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Example

    public class RandomClass {

    // ...

    }
    javac

    View full-size slide

  89. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Example

    public class RandomClass {

    // ...

    }
    javac
    ExampleProcessor

    View full-size slide

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

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

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

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

  94. Processing Rounds
    @Example

    public class RandomClass {

    // ...

    }
    ExampleProcessor OtherProcessor
    javac
    RandomClass.class
    out/
    @Other

    public class GeneratedClass {

    // ...

    }
    @Example

    public class RandomClass {

    // ...

    }

    View full-size slide

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

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

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

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

  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
    OtherProcessor

    View full-size 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
    OtherProcessor
    public class OtherGenClass {

    // ...

    }

    View full-size 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
    public class OtherGenClass {

    // ...

    }

    View full-size 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
    public class OtherGenClass {

    // ...

    }

    View full-size 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 full-size 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 {

    // ...

    }
    OtherGenClass.class
    public class OtherGenClass {

    // ...

    }

    View full-size 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 {

    // ...

    }
    ExampleProcessor
    OtherGenClass.class
    public class OtherGenClass {

    // ...

    }

    View full-size 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 {

    // ...

    }
    OtherProcessor
    OtherGenClass.class
    public class OtherGenClass {

    // ...

    }

    View full-size 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 {

    // ...

    }
    OtherGenClass.class
    public class OtherGenClass {

    // ...

    }

    View full-size 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 {

    // ...

    }
    OtherGenClass.class
    public class OtherGenClass {

    // ...

    }

    View full-size 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 {

    // ...

    }
    public class OtherGenClass {

    // ...

    }
    OtherGenClass.class
    public class OtherGenClass {

    // ...

    }

    View full-size slide

  110. JavaCompiler

    View full-size slide

  111. ExampleProcessor
    JavaCompiler
    OtherProcessor

    View full-size slide

  112. ProcessingEnvironment
    ExampleProcessor
    JavaCompiler
    Filer
    OtherProcessor

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

  130. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }


    View full-size slide

  131. Parsing and Generation
    @Override public boolean process(

    Set extends TypeElement> annotations,

    RoundEnvironment env) {





    return false;

    }




    List models = parseExampleAnnotations(env);

    View full-size slide

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

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

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

  135. Parsing and Generation
    Element

    View full-size slide

  136. Parsing and Generation
    Element

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

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

  142. Bring Your Own Dependencies

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

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

  150. 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 full-size slide

  151. AutoService
    • Remember “ServiceLoader finds and creates”

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  155. Testing and Debugging

    View full-size slide

  156. Testing and Debugging
    • Google to the rescue!

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  160. final class TestProcessors {

    static Iterable extends Processor> exampleProcessors() {

    return Collections.singletonList(

    new ExampleProcessor()

    );

    }

    }

    View full-size slide

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

    "package test;",

    "import com.example.Example;",

    "@Example",

    "public class ExampleClass {",

    "}"

    );

    View full-size slide

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

  163. JavaFileObject inputFile =

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

    package test;

    import com.example.Example;

    @Example

    public class ExampleClass {

    }
    """
    .stripIndent()

    View full-size slide

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

    "package test;",

    "import com.example.Other;",

    "@Other",

    "public class GeneratedClass {",

    "}"

    );

    View full-size slide

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

  166. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View full-size slide

  167. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View full-size slide

  168. ASSERT.about(javaSource())

    .that(inputFile)

    .processedWith(exampleProcessors())

    .compilesWithoutError()

    .and()

    .generatesSources(expectedFile);

    View full-size slide

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

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

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

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

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

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