96
Write code that writes code! A beginner’s guide to annotation processing.

Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Embed Size (px)

Citation preview

Page 1: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Write code that writes code!A beginner’s guide to annotation processing.

Page 2: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Obligatory Speaker Details

• Software Engineer for Bandcamp

• I’m from the US, but am living in Europe for now. (working remotely)

• I have a dog named Watson. On weekends, we walk across the Netherlands together.

Page 3: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Questions we ask ourselves in the beginning.

• What is an annotation, and what is annotation processing?

• Why would I want to process annotations?

• How do I make something cool? Maybe a ButterKnife clone?

Page 4: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

–http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html

“…an annotation is a form of syntactic metadata…”

Annotations

Page 5: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

–http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html

“Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools and libraries, which can in

turn affect the semantics of the running program.”

Annotations

Page 6: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Annotations

• You’ve seen them before (e.g. @Override, @Deprecated, etc.)

• They allow you to decorate code with information about the code (ie: they are meta data)

• Kind of like comments, but they are more machine readable than human readable.

• Annotations can be used by the JDK, third party libraries, or custom tools.

• You can create your own annotations.

Page 7: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Custom annotations are useless..

… until you use them.

Page 8: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Custom Annotations• Useless

Page 9: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

• Useless (until you actually use them…)

Custom Annotations• Useless

Page 10: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

• Useless

• Run-time - with reflection

• Compile-time “Annotation Processor”

• Useless (until you actually use them…)

Custom Annotations• Useless

Page 11: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

–Everyone

“Reflection is slow and you should never use it.”

Page 12: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

–Smart People

“Reflection is slow and you should try to avoid using it on the main thread.”

Page 13: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Annotation Processors

• Operate at build-time, rather than run-time.

• Are executed by the “annotation processing tool” (apt)

• Must be part of a plain-old java library, without direct dependencies on Android-specific stuff.

• Extend from javax.annotation.processing.AbstractProcessor

Page 14: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Annotation Processing

List unprocessed source files with

annotations.

Register Annotation Processors

Any Processors for them?

Run ProcessorsCompileNo* Yes

Page 15: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Annotation Processing

* If a processor was asked to process on a given round, it will be asked to process on subsequent rounds, including the last round, even if there are no annotations for it to process. https://docs.oracle.com/javase/8/docs/api/javax/annotation/processing/Processor.html

List unprocessed source files with

annotations.

Register Annotation Processors

Any Processors for them?

Run ProcessorsCompileNo* Yes

Page 16: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Why would you want to make one?

• Boilerplate Reduction

• Reducing Boilerplate

• Reduced Boilerplate

• ….

• It’s pretty cool.

Page 17: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Let’s make one.

Page 18: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

“Soup Ladle”

• Wanted something that sounded like Butter Knife, but was a different utensil.

• I like Soup.

• Ladles are big spoons.

• Big spoon = more soup in my face at once.

Page 19: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Soup Ladle Goals• Allow for view binding with an annotation: @Bind(R.id.some_id) View fieldName;

• Perform the binding easily using a one liner in onCreate:SoupLadle.bind(this);

• That’s it.. we are reinventing the wheel for learning’s sake and don’t need to go all in.

Page 20: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Approach

1. Define the @Bind annotation.

2. Extend AbstractProcessor to create our annotation processor for @Bind.

3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes.

4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.

Page 21: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Approach

1. Define the @Bind annotation.

2. Extend AbstractProcessor to create our annotation processor for @Bind.

3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes.

4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.

Page 22: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Define @Bind

@Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Bind { int value();}

Page 23: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Define @Bind

@Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Bind { int value();}

Page 24: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Define @Bind

@Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Bind { int value();}

Page 25: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Define @Bind

@Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Bind { int value();}

Page 26: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Define @Bind

@Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface Bind { int value();}

Page 27: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Approach

1. Define the @Bind annotation.

2. Extend AbstractProcessor to create our annotation processor for @Bind.

3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes.

4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.

Page 28: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Extending AbstractProcessorpublic class AnnotationProcessor extends AbstractProcessor { private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> result = new HashSet<>(); result.add(Bind.class.getCanonicalName()); return result; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }

Page 29: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Extending AbstractProcessorpublic class AnnotationProcessor extends AbstractProcessor { private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> result = new HashSet<>(); result.add(Bind.class.getCanonicalName()); return result; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }

Page 30: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Extending AbstractProcessorpublic class AnnotationProcessor extends AbstractProcessor { private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> result = new HashSet<>(); result.add(Bind.class.getCanonicalName()); return result; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }

Page 31: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Extending AbstractProcessorpublic class AnnotationProcessor extends AbstractProcessor { private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> result = new HashSet<>(); result.add(Bind.class.getCanonicalName()); return result; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }

Page 32: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Extending AbstractProcessorpublic class AnnotationProcessor extends AbstractProcessor { private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> result = new HashSet<>(); result.add(Bind.class.getCanonicalName()); return result; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }

Page 33: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Extending AbstractProcessorpublic class AnnotationProcessor extends AbstractProcessor { private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> result = new HashSet<>(); result.add(Bind.class.getCanonicalName()); return result; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }

Page 34: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Extending AbstractProcessorpublic class AnnotationProcessor extends AbstractProcessor { private Filer mFiler; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); mFiler = processingEnv.getFiler(); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() { HashSet<String> result = new HashSet<>(); result.add(Bind.class.getCanonicalName()); return result; } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // logic goes here // as does the actual code generation // // we’ll get to this stuff in a little bit } }

Page 35: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Approach

1. Define the @Bind annotation.

2. Extend AbstractProcessor to create our annotation processor for @Bind.

3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes.

4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.

Page 36: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Processing…

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (annotations.isEmpty()) { return true; } Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>(); for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) { VariableElement variable = (VariableElement) e; TypeElement parent = (TypeElement) variable.getEnclosingElement(); List<VariableElement> members; if (bindingClasses.containsKey(parentClass)) { members = bindingClasses.get(parentClass); } else { members = new ArrayList<>(); bindingClasses.put(parentClass, members); } members.add(variable); }

// .. generate code .. }

Page 37: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Processing…

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (annotations.isEmpty()) { return true; } Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>(); for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) { VariableElement variable = (VariableElement) e; TypeElement parent = (TypeElement) variable.getEnclosingElement(); List<VariableElement> members; if (bindingClasses.containsKey(parentClass)) { members = bindingClasses.get(parentClass); } else { members = new ArrayList<>(); bindingClasses.put(parentClass, members); } members.add(variable); }

// .. generate code .. }

Page 38: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Processing…

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (annotations.isEmpty()) { return true; } Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>(); for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) { VariableElement variable = (VariableElement) e; TypeElement parent = (TypeElement) variable.getEnclosingElement(); List<VariableElement> members; if (bindingClasses.containsKey(parentClass)) { members = bindingClasses.get(parentClass); } else { members = new ArrayList<>(); bindingClasses.put(parentClass, members); } members.add(variable); }

// .. generate code .. }

Page 39: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Processing…

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if (annotations.isEmpty()) { return true; } Map<TypeElement, List<VariableElement>> bindingClasses = new HashMap<>(); for (Element e : roundEnv.getElementsAnnotatedWith(Bind.class)) { VariableElement variable = (VariableElement) e; TypeElement parent = (TypeElement) variable.getEnclosingElement(); List<VariableElement> members; if (bindingClasses.containsKey(parentClass)) { members = bindingClasses.get(parentClass); } else { members = new ArrayList<>(); bindingClasses.put(parentClass, members); } members.add(variable); }

// .. generate code .. }

Page 40: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Approach

1. Define the @Bind annotation.

2. Extend AbstractProcessor to create our annotation processor for @Bind.

3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes.

4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.

Page 41: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); } catch (IOException e) { throw new RuntimeException(e); }

}

Page 42: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); } catch (IOException e) { throw new RuntimeException(e); }

}

Page 43: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); } catch (IOException e) { throw new RuntimeException(e); }

}

Page 44: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); } catch (IOException e) { throw new RuntimeException(e); }

}

😱

Page 45: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

There has to be a better way!?

Page 46: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

There is a better way.

Page 47: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Introducing: JavaPoet

Page 48: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Introducing: JavaPoetBy Square (Of Course)

Page 49: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

JavaPoet

• Builder-pattern approach to programmatically defining a class and its fields/methods.

• Automatically manages the classes needed for import.

• When you’re ready, it will write clean & readable Java source to an OutputStream/Writer.

Page 50: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

JavaPoet - Hello WorldTypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("System.out.println($S + args[0])", "Hello: ") .build());JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);

package jwf.soupladle;import java.lang.String;public class HelloWorld { public static void main(String[] args) { System.out.println("Hello: " + args[0]); } }

Page 51: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

JavaPoet - Hello WorldTypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("System.out.println($S + args[0])", "Hello: ") .build());JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);

package jwf.soupladle;import java.lang.String;public class HelloWorld { public static void main(String[] args) { System.out.println("Hello: " + args[0]); }}

Page 52: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

JavaPoet - Hello WorldTypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("System.out.println($S + args[0])", "Hello: ") .build());JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);

package jwf.soupladle;import java.lang.String;public class HelloWorld { public static void main(String[] args) { System.out.println("Hello: " + args[0]); } }

Page 53: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

JavaPoet - Hello WorldTypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("System.out.println($S + args[0])", "Hello: ") .build());JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);

package jwf.soupladle;import java.lang.String;public class HelloWorld { public static void main(String[] args) { System.out.println("Hello: " + args[0]); } }

Page 54: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

JavaPoet - Hello World

👏

TypeSpec.Builder helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("System.out.println($S + args[0])", "Hello: ") .build());JavaFile.builder("jwf.soupladle", helloWorld.build()).build().writeTo(System.out);

package jwf.soupladle;import java.lang.String;public class HelloWorld { public static void main(String[] args) { System.out.println("Hello: " + args[0]); } }

Page 55: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Approach

1. Define the @Bind annotation.

2. Extend AbstractProcessor to create our annotation processor for @Bind.

3. Within our processor: scan for all fields with @Bind, keeping track of their parent classes.

4. Generate SoupLadle.java with .bind methods for each parent class containing bound fields.

Page 56: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 57: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 58: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 59: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 60: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 61: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 62: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

"target.$L = ($T) target.findViewById($L)"

Page 63: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

"target.$L = ($T) target.findViewById($L)"

Page 64: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

"target.$L = ($T) target.findViewById($L)"

Page 65: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

"target.$L = ($T) target.findViewById($L)"

Page 66: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 67: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 68: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 69: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

👀

Page 70: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 71: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // .. process annotations ..

try { JavaFileObject jfo = mFiler.createSourceFile("jwf.soupladle.SoupLadle"); TypeSpec.Builder soupLadleBuilder = TypeSpec.classBuilder("SoupLadle") .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (Map.Entry<TypeElement, List<VariableElement>> binding : bindingClasses.entrySet()) { TypeName typeParameter = ClassName.get(binding.getKey()); MethodSpec.Builder bindingBuilder = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addParameter(typeParameter, "target"); List<VariableElement> members = binding.getValue(); for (VariableElement member : members) { Bind annotation = member.getAnnotation(Bind.class); TypeName castClass = ClassName.get(member.asType()); bindingBuilder.addStatement("target.$L = ($T) target.findViewById($L)", member.getSimpleName().toString(), castClass, annotation.value()); } soupLadleBuilder.addMethod(bindingBuilder.build()); } AnnotationSpec suppressIdentifierWarningAnnotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "ResourceType").build(); soupLadleBuilder.addAnnotation(suppressIdentifierWarningAnnotation); JavaFile file = JavaFile.builder("jwf.soupladle", soupLadleBuilder.build()).build(); Writer out = jfo.openWriter(); file.writeTo(out); out.flush(); out.close(); } catch (IOException e) { throw new RuntimeException(e); } return true; }

Page 72: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

We’ve got an annotation processor now!

Page 73: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

How do we tell the build process about it?

Page 74: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Project/Module Config

• The annotation processor and binding annotation class need to live in a “regular” java module.

• Add the android-apt gradle plugin to your root build.gradle.

• Add dependency records to your app’s build.gradle.

Page 75: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Project/Module Config

• The annotation processor and binding annotation class need to live in a “regular” java module.

• Add the android-apt gradle plugin to your root build.gradle.

• Add dependency records to your app’s build.gradle.

Page 76: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

SoupLadle Module

Page 77: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

SoupLadle Module

apply plugin: 'java'dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.squareup:javapoet:1.7.0'}

Page 78: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

SoupLadle Module

jwf.soupladle.AnnotationProcessor

Page 79: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

SoupLadle Module

include ':app', ':library'

Page 80: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Project/Module Config

• The annotation processor and binding annotation class need to live in a “regular” java module.

• Add the android-apt gradle plugin to your root build.gradle.

• Add dependency records to your app’s build.gradle.

Page 81: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Project build.gradlebuildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.3' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } } allprojects { repositories { jcenter() }} task clean(type: Delete) { delete rootProject.buildDir}

Page 82: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Project build.gradlebuildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.3' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } } allprojects { repositories { jcenter() }} task clean(type: Delete) { delete rootProject.buildDir}

Page 83: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Project/Module Config

• The annotation processor and binding annotation class need to live in a “regular” java module.

• Add the android-apt gradle plugin to your root build.gradle.

• Add dependency records to your app’s build.gradle.

Page 84: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

App build.gradleapply plugin: 'com.android.application'apply plugin: 'com.neenbedankt.android-apt'android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "jwf.soupladle.example" minSdkVersion 16 targetSdkVersion 24 versionCode 1 versionName "1.0" } buildTypes { // .. your build types .. } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:24.1.1' apt project(':library') provided project(':library') }

Page 85: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

App build.gradleapply plugin: 'com.android.application'apply plugin: 'com.neenbedankt.android-apt'android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "jwf.soupladle.example" minSdkVersion 16 targetSdkVersion 24 versionCode 1 versionName "1.0" } buildTypes { // .. your build types .. }} dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:24.1.1' apt project(':library') provided project(':library') }

Page 86: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

App build.gradleapply plugin: 'com.android.application'apply plugin: 'com.neenbedankt.android-apt'android { compileSdkVersion 24 buildToolsVersion "24.0.2" defaultConfig { applicationId "jwf.soupladle.example" minSdkVersion 16 targetSdkVersion 24 versionCode 1 versionName "1.0" } buildTypes { // .. your build types .. }} dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:24.1.1' apt project(':library') provided project(':library') }

Page 87: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Let’s try using it!

Page 88: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="jwf.soupladle.example.MainActivity"> <TextView android:id="@+id/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!"/></RelativeLayout>

Page 89: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

MainActivity.java

package jwf.soupladle.example;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.TextView;import jwf.soupladle.Bind; public class MainActivity extends AppCompatActivity { @Bind(R.id.hello_world) public TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}

Page 90: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

MainActivity.java

package jwf.soupladle.example;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.TextView;import jwf.soupladle.Bind; public class MainActivity extends AppCompatActivity { @Bind(R.id.hello_world) public TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}

Page 91: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

rebuild project…

Page 92: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

A wild SoupLadle.java Appears!

package jwf.soupladle;import android.widget.TextView;import java.lang.SuppressWarnings; import jwf.soupladle.example.MainActivity;@SuppressWarnings("ResourceType") public final class SoupLadle { public static final void bind(MainActivity target) { target.textView = (TextView) target.findViewById(2131427412); }}

Page 93: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

MainActivity.java

package jwf.soupladle.example;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.TextView;import jwf.soupladle.Bind;import jwf.soupladle.SoupLadle;public class MainActivity extends AppCompatActivity { @Bind(R.id.hello_world) public TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SoupLadle.bind(this); textView.setText("The binding worked!"); }}

Page 94: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein
Page 95: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein
Page 96: Write code that writes code! A beginner's guide to Annotation Processing - Jason Feinstein

Thank you! Questions?Twitter: @jasonwyatt

github.com/jasonwyatt bandcamp.com/jasonwyatt

Source Code available at: github.com/jasonwyatt/Soup-Ladle