SlideShare a Scribd company logo
Write code that writes code!
A beginner’s guide to annotation processing.
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.
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?
–http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html
“…an annotation is a form of syntactic
metadata…”
Annotations
–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
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.
Custom annotations are
useless..
… until you use them.
Custom Annotations
• Useless
• Useless (until you actually use them…)
Custom Annotations
• Useless
• Useless
• Run-time - with reflection
• Compile-time “Annotation Processor”
• Useless (until you actually use them…)
Custom Annotations
• Useless
–Everyone
“Reflection is slow and you should never use it.”
–Smart People
“Reflection is slow and you should try to avoid
using it on the main thread.”
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
Annotation Processing
List unprocessed
source files with
annotations.
Register
Annotation
Processors
Any
Processors
for them?
Run ProcessorsCompile
No* Yes
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 ProcessorsCompile
No* Yes
Why would you want to make one?
• Boilerplate Reduction
• Reducing Boilerplate
• Reduced Boilerplate
• ….
• It’s pretty cool.
Let’s make one.
“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.
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.
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.
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.
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
Define @Bind
@Target(ElementType.FIELD)

@Retention(RetentionPolicy.SOURCE)

public @interface Bind {

int value();

}
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.
Extending AbstractProcessor
public 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
}
}
Extending AbstractProcessor
public 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
}
}
Extending AbstractProcessor
public 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
}
}
Extending AbstractProcessor
public 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
}
}
Extending AbstractProcessor
public 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
}
}
Extending AbstractProcessor
public 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
}
}
Extending AbstractProcessor
public 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
}
}
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.
Processing…
@Override

public 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 ..
}
Processing…
@Override

public 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 ..
}
Processing…
@Override

public 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 ..
}
Processing…
@Override

public 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 ..
}
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.
@Override

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

}
}
@Override

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

}
}
@Override

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

}
}
@Override

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

}
}
😱
There has to be a better way!?
There is a better way.
Introducing: JavaPoet
Introducing: JavaPoet
By Square (Of Course)
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.
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]);

}

}

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

}

}

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

}

}

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

}

}

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

}

}

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

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

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

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

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

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

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

public 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)"
@Override

public 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)"
@Override

public 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)"
@Override

public 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)"
@Override

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

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

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

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

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

public 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;
}
We’ve got an annotation
processor now!
How do we tell the build
process about it?
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.
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.
SoupLadle Module
SoupLadle Module


apply plugin: 'java'



dependencies {

compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.squareup:javapoet:1.7.0'

}
SoupLadle Module
jwf.soupladle.AnnotationProcessor
SoupLadle Module
include ':app', ':library'
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.
Project build.gradle
buildscript {

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

}
Project build.gradle
buildscript {

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

}
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.
App build.gradle
apply 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')

}
App build.gradle
apply 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')

}
App build.gradle
apply 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')

}
Let’s try using it!
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>

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

}

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

}

}
rebuild project…
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);

}

}

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

}

}
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 - Jason Feinstein
Thank you! Questions?
Twitter: @jasonwyatt
github.com/jasonwyatt
bandcamp.com/jasonwyatt
Source Code available at:
github.com/jasonwyatt/Soup-Ladle
Ad

Recommended

Build an App with Blindfold - Britt Barak
Build an App with Blindfold - Britt Barak
DroidConTLV
 
Engineering Wunderlist for Android - Ceasr Valiente, 6Wunderkinder
Engineering Wunderlist for Android - Ceasr Valiente, 6Wunderkinder
DroidConTLV
 
Think Async: Understanding the Complexity of Multithreading - Avi Kabizon & A...
Think Async: Understanding the Complexity of Multithreading - Avi Kabizon & A...
DroidConTLV
 
Servlet and JSP
Servlet and JSP
Gary Yeh
 
Session And Cookies In Servlets - Java
Session And Cookies In Servlets - Java
JainamParikh3
 
Paris Tech Meetup talk : Troubles start at version 1.0
Paris Tech Meetup talk : Troubles start at version 1.0
Laurent Cerveau
 
Akka lsug skills matter
Akka lsug skills matter
Skills Matter
 
Elements for an iOS Backend
Elements for an iOS Backend
Laurent Cerveau
 
Jsp & Ajax
Jsp & Ajax
Ang Chen
 
Servlet sessions
Servlet sessions
vantinhkhuc
 
SenchaCon 2016: How to Auto Generate a Back-end in Minutes - Per Minborg, Emi...
SenchaCon 2016: How to Auto Generate a Back-end in Minutes - Per Minborg, Emi...
Sencha
 
Spring 4 Web App
Spring 4 Web App
Rossen Stoyanchev
 
Java EE 01-Servlets and Containers
Java EE 01-Servlets and Containers
Fernando Gil
 
Dropwizard Internals
Dropwizard Internals
carlo-rtr
 
Flask & Flask-restx
Flask & Flask-restx
ammaraslam18
 
Using MongoDB with the .Net Framework
Using MongoDB with the .Net Framework
Stefano Paluello
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
Fabio Collini
 
RxJS and Reactive Programming - Modern Web UI - May 2015
RxJS and Reactive Programming - Modern Web UI - May 2015
Ben Lesh
 
Java web application development
Java web application development
RitikRathaur
 
Mongo db rev001.
Mongo db rev001.
Rich Helton
 
Servlets & jdbc
Servlets & jdbc
Siva Priya
 
AJAX
AJAX
Mukesh Tekwani
 
ASP.Net 5 and C# 6
ASP.Net 5 and C# 6
Andy Butland
 
Discovering the Service Fabric's actor model
Discovering the Service Fabric's actor model
Massimo Bonanni
 
Jquery Ajax
Jquery Ajax
Anand Kumar Rajana
 
Academy PRO: HTML5 Data storage
Academy PRO: HTML5 Data storage
Binary Studio
 
JavaCro'14 - Building interactive web applications with Vaadin – Peter Lehto
JavaCro'14 - Building interactive web applications with Vaadin – Peter Lehto
HUJAK - Hrvatska udruga Java korisnika / Croatian Java User Association
 
Will it run or will it not run? Background processes in Android 6 - Anna Lifs...
Will it run or will it not run? Background processes in Android 6 - Anna Lifs...
DroidConTLV
 
3 things every Android developer must know about Microsoft - Ido Volff, Micro...
3 things every Android developer must know about Microsoft - Ido Volff, Micro...
DroidConTLV
 
Intro to Dependency Injection - Or bar
Intro to Dependency Injection - Or bar
DroidConTLV
 

More Related Content

What's hot (19)

Jsp & Ajax
Jsp & Ajax
Ang Chen
 
Servlet sessions
Servlet sessions
vantinhkhuc
 
SenchaCon 2016: How to Auto Generate a Back-end in Minutes - Per Minborg, Emi...
SenchaCon 2016: How to Auto Generate a Back-end in Minutes - Per Minborg, Emi...
Sencha
 
Spring 4 Web App
Spring 4 Web App
Rossen Stoyanchev
 
Java EE 01-Servlets and Containers
Java EE 01-Servlets and Containers
Fernando Gil
 
Dropwizard Internals
Dropwizard Internals
carlo-rtr
 
Flask & Flask-restx
Flask & Flask-restx
ammaraslam18
 
Using MongoDB with the .Net Framework
Using MongoDB with the .Net Framework
Stefano Paluello
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
Fabio Collini
 
RxJS and Reactive Programming - Modern Web UI - May 2015
RxJS and Reactive Programming - Modern Web UI - May 2015
Ben Lesh
 
Java web application development
Java web application development
RitikRathaur
 
Mongo db rev001.
Mongo db rev001.
Rich Helton
 
Servlets & jdbc
Servlets & jdbc
Siva Priya
 
AJAX
AJAX
Mukesh Tekwani
 
ASP.Net 5 and C# 6
ASP.Net 5 and C# 6
Andy Butland
 
Discovering the Service Fabric's actor model
Discovering the Service Fabric's actor model
Massimo Bonanni
 
Jquery Ajax
Jquery Ajax
Anand Kumar Rajana
 
Academy PRO: HTML5 Data storage
Academy PRO: HTML5 Data storage
Binary Studio
 
JavaCro'14 - Building interactive web applications with Vaadin – Peter Lehto
JavaCro'14 - Building interactive web applications with Vaadin – Peter Lehto
HUJAK - Hrvatska udruga Java korisnika / Croatian Java User Association
 
Jsp & Ajax
Jsp & Ajax
Ang Chen
 
Servlet sessions
Servlet sessions
vantinhkhuc
 
SenchaCon 2016: How to Auto Generate a Back-end in Minutes - Per Minborg, Emi...
SenchaCon 2016: How to Auto Generate a Back-end in Minutes - Per Minborg, Emi...
Sencha
 
Java EE 01-Servlets and Containers
Java EE 01-Servlets and Containers
Fernando Gil
 
Dropwizard Internals
Dropwizard Internals
carlo-rtr
 
Flask & Flask-restx
Flask & Flask-restx
ammaraslam18
 
Using MongoDB with the .Net Framework
Using MongoDB with the .Net Framework
Stefano Paluello
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
Fabio Collini
 
RxJS and Reactive Programming - Modern Web UI - May 2015
RxJS and Reactive Programming - Modern Web UI - May 2015
Ben Lesh
 
Java web application development
Java web application development
RitikRathaur
 
Mongo db rev001.
Mongo db rev001.
Rich Helton
 
Servlets & jdbc
Servlets & jdbc
Siva Priya
 
ASP.Net 5 and C# 6
ASP.Net 5 and C# 6
Andy Butland
 
Discovering the Service Fabric's actor model
Discovering the Service Fabric's actor model
Massimo Bonanni
 
Academy PRO: HTML5 Data storage
Academy PRO: HTML5 Data storage
Binary Studio
 

Viewers also liked (15)

Will it run or will it not run? Background processes in Android 6 - Anna Lifs...
Will it run or will it not run? Background processes in Android 6 - Anna Lifs...
DroidConTLV
 
3 things every Android developer must know about Microsoft - Ido Volff, Micro...
3 things every Android developer must know about Microsoft - Ido Volff, Micro...
DroidConTLV
 
Intro to Dependency Injection - Or bar
Intro to Dependency Injection - Or bar
DroidConTLV
 
Android is going to Go! - Android and goland - Almog Baku
Android is going to Go! - Android and goland - Almog Baku
DroidConTLV
 
Creating killer apps powered by watson cognitive services - Ronen Siman-Tov, IBM
Creating killer apps powered by watson cognitive services - Ronen Siman-Tov, IBM
DroidConTLV
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
DroidConTLV
 
Cognitive interaction using Wearables - Eyal herman, IBM
Cognitive interaction using Wearables - Eyal herman, IBM
DroidConTLV
 
Good Rules for Bad Apps - Shem magnezi
Good Rules for Bad Apps - Shem magnezi
DroidConTLV
 
Mobile SDKs: Use with Caution - Ori Lentzitzky
Mobile SDKs: Use with Caution - Ori Lentzitzky
DroidConTLV
 
Android Application Optimization: Overview and Tools - Oref Barad, AVG
Android Application Optimization: Overview and Tools - Oref Barad, AVG
DroidConTLV
 
Context is Everything - Royi Benyossef
Context is Everything - Royi Benyossef
DroidConTLV
 
Set it and forget it: Let the machine learn its job - Guy Baron, Vonage
Set it and forget it: Let the machine learn its job - Guy Baron, Vonage
DroidConTLV
 
Knock knock! Who's there? Doze. - Yonatan Levin
Knock knock! Who's there? Doze. - Yonatan Levin
DroidConTLV
 
Optimize your delivery and quality with the right release methodology and too...
Optimize your delivery and quality with the right release methodology and too...
DroidConTLV
 
Android Continuous Integration and Automation - Enrique Lopez Manas, Sixt
Android Continuous Integration and Automation - Enrique Lopez Manas, Sixt
DroidConTLV
 
Will it run or will it not run? Background processes in Android 6 - Anna Lifs...
Will it run or will it not run? Background processes in Android 6 - Anna Lifs...
DroidConTLV
 
3 things every Android developer must know about Microsoft - Ido Volff, Micro...
3 things every Android developer must know about Microsoft - Ido Volff, Micro...
DroidConTLV
 
Intro to Dependency Injection - Or bar
Intro to Dependency Injection - Or bar
DroidConTLV
 
Android is going to Go! - Android and goland - Almog Baku
Android is going to Go! - Android and goland - Almog Baku
DroidConTLV
 
Creating killer apps powered by watson cognitive services - Ronen Siman-Tov, IBM
Creating killer apps powered by watson cognitive services - Ronen Siman-Tov, IBM
DroidConTLV
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
DroidConTLV
 
Cognitive interaction using Wearables - Eyal herman, IBM
Cognitive interaction using Wearables - Eyal herman, IBM
DroidConTLV
 
Good Rules for Bad Apps - Shem magnezi
Good Rules for Bad Apps - Shem magnezi
DroidConTLV
 
Mobile SDKs: Use with Caution - Ori Lentzitzky
Mobile SDKs: Use with Caution - Ori Lentzitzky
DroidConTLV
 
Android Application Optimization: Overview and Tools - Oref Barad, AVG
Android Application Optimization: Overview and Tools - Oref Barad, AVG
DroidConTLV
 
Context is Everything - Royi Benyossef
Context is Everything - Royi Benyossef
DroidConTLV
 
Set it and forget it: Let the machine learn its job - Guy Baron, Vonage
Set it and forget it: Let the machine learn its job - Guy Baron, Vonage
DroidConTLV
 
Knock knock! Who's there? Doze. - Yonatan Levin
Knock knock! Who's there? Doze. - Yonatan Levin
DroidConTLV
 
Optimize your delivery and quality with the right release methodology and too...
Optimize your delivery and quality with the right release methodology and too...
DroidConTLV
 
Android Continuous Integration and Automation - Enrique Lopez Manas, Sixt
Android Continuous Integration and Automation - Enrique Lopez Manas, Sixt
DroidConTLV
 
Ad

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

Annotation Processing
Annotation Processing
Jintin Lin
 
Java Annotation Processing: A Beginner Walkthrough
Java Annotation Processing: A Beginner Walkthrough
Mahfuz Islam Bhuiyan
 
Annotation processing in android
Annotation processing in android
Zhe-Hao Hu
 
Custom Annotations in Java with Project Lombok
Custom Annotations in Java with Project Lombok
Yann-Gaël Guéhéneuc
 
Annotation Processing in Android
Annotation Processing in Android
emanuelez
 
Ow2 Utilities - The Swiss Army Knife Of Ow2 Projects
Ow2 Utilities - The Swiss Army Knife Of Ow2 Projects
Guillaume Sauthier
 
Daggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processor
Bartosz Kosarzycki
 
Annotation processor and compiler plugin
Annotation processor and compiler plugin
Oleksandr Radchykov
 
Infinum Android Talks #02 - How to write an annotation processor in Android
Infinum Android Talks #02 - How to write an annotation processor in Android
Infinum
 
Code transformation With Spoon
Code transformation With Spoon
Gérard Paligot
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Java User Group Latvia
 
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
Jorge Hidalgo
 
Java Custom Annotations- Part1
Java Custom Annotations- Part1
Mohammad Sabir Khan
 
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
Jorge Hidalgo
 
Annotation processing
Annotation processing
Benjamin Cheng
 
Annotation Processing - Demystifying Java's Dark Arts
Annotation Processing - Demystifying Java's Dark Arts
James Kirkbride
 
Java annotations
Java annotations
FAROOK Samath
 
Annotation processing
Annotation processing
Felipe Theodoro
 
How to code to code less
How to code to code less
Anton Novikau
 
Annotations in Java
Annotations in Java
Kirill Kulakov
 
Annotation Processing
Annotation Processing
Jintin Lin
 
Java Annotation Processing: A Beginner Walkthrough
Java Annotation Processing: A Beginner Walkthrough
Mahfuz Islam Bhuiyan
 
Annotation processing in android
Annotation processing in android
Zhe-Hao Hu
 
Custom Annotations in Java with Project Lombok
Custom Annotations in Java with Project Lombok
Yann-Gaël Guéhéneuc
 
Annotation Processing in Android
Annotation Processing in Android
emanuelez
 
Ow2 Utilities - The Swiss Army Knife Of Ow2 Projects
Ow2 Utilities - The Swiss Army Knife Of Ow2 Projects
Guillaume Sauthier
 
Daggerate your code - Write your own annotation processor
Daggerate your code - Write your own annotation processor
Bartosz Kosarzycki
 
Annotation processor and compiler plugin
Annotation processor and compiler plugin
Oleksandr Radchykov
 
Infinum Android Talks #02 - How to write an annotation processor in Android
Infinum Android Talks #02 - How to write an annotation processor in Android
Infinum
 
Code transformation With Spoon
Code transformation With Spoon
Gérard Paligot
 
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Marvel of Annotation Preprocessing in Java by Alexey Buzdin
Java User Group Latvia
 
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
JavaOne 2017 CON3282 - Code Generation with Annotation Processors: State of t...
Jorge Hidalgo
 
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
JavaOne 2014 - CON2013 - Code Generation in the Java Compiler: Annotation Pro...
Jorge Hidalgo
 
Annotation Processing - Demystifying Java's Dark Arts
Annotation Processing - Demystifying Java's Dark Arts
James Kirkbride
 
How to code to code less
How to code to code less
Anton Novikau
 
Ad

More from DroidConTLV (20)

Mobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, Nike
DroidConTLV
 
Doing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra Technologies
DroidConTLV
 
No more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola Solutions
DroidConTLV
 
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
DroidConTLV
 
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
DroidConTLV
 
MVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, Lightricks
DroidConTLV
 
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
DroidConTLV
 
Building Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice Ninja
DroidConTLV
 
New Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy Zukanov
DroidConTLV
 
Designing a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, Gett
DroidConTLV
 
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
DroidConTLV
 
Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev
Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev
DroidConTLV
 
Flutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, Tikal
DroidConTLV
 
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
DroidConTLV
 
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
DroidConTLV
 
DroidconTLV 2019
DroidconTLV 2019
DroidConTLV
 
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
DroidConTLV
 
Introduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, Wix
DroidConTLV
 
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
DroidConTLV
 
Educating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz Tamir
DroidConTLV
 
Mobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, Nike
DroidConTLV
 
Doing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra Technologies
DroidConTLV
 
No more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola Solutions
DroidConTLV
 
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
DroidConTLV
 
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell
DroidConTLV
 
MVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, Lightricks
DroidConTLV
 
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
DroidConTLV
 
Building Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice Ninja
DroidConTLV
 
New Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy Zukanov
DroidConTLV
 
Designing a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, Gett
DroidConTLV
 
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
DroidConTLV
 
Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev
Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev
DroidConTLV
 
Flutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, Tikal
DroidConTLV
 
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
DroidConTLV
 
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
DroidConTLV
 
DroidconTLV 2019
DroidconTLV 2019
DroidConTLV
 
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
DroidConTLV
 
Introduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, Wix
DroidConTLV
 
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
DroidConTLV
 
Educating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz Tamir
DroidConTLV
 

Recently uploaded (20)

OWASP Barcelona 2025 Threat Model Library
OWASP Barcelona 2025 Threat Model Library
PetraVukmirovic
 
You are not excused! How to avoid security blind spots on the way to production
You are not excused! How to avoid security blind spots on the way to production
Michele Leroux Bustamante
 
Cyber Defense Matrix Workshop - RSA Conference
Cyber Defense Matrix Workshop - RSA Conference
Priyanka Aash
 
FIDO Seminar: Authentication for a Billion Consumers - Amazon.pptx
FIDO Seminar: Authentication for a Billion Consumers - Amazon.pptx
FIDO Alliance
 
Securing Account Lifecycles in the Age of Deepfakes.pptx
Securing Account Lifecycles in the Age of Deepfakes.pptx
FIDO Alliance
 
OpenACC and Open Hackathons Monthly Highlights June 2025
OpenACC and Open Hackathons Monthly Highlights June 2025
OpenACC
 
cnc-processing-centers-centateq-p-110-en.pdf
cnc-processing-centers-centateq-p-110-en.pdf
AmirStern2
 
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
Fwdays
 
Connecting Data and Intelligence: The Role of FME in Machine Learning
Connecting Data and Intelligence: The Role of FME in Machine Learning
Safe Software
 
Using the SQLExecutor for Data Quality Management: aka One man's love for the...
Using the SQLExecutor for Data Quality Management: aka One man's love for the...
Safe Software
 
Information Security Response Team Nepal_npCERT_Vice_President_Sudan_Jha.pdf
Information Security Response Team Nepal_npCERT_Vice_President_Sudan_Jha.pdf
ICT Frame Magazine Pvt. Ltd.
 
PyCon SG 25 - Firecracker Made Easy with Python.pdf
PyCon SG 25 - Firecracker Made Easy with Python.pdf
Muhammad Yuga Nugraha
 
ReSTIR [DI]: Spatiotemporal reservoir resampling for real-time ray tracing ...
ReSTIR [DI]: Spatiotemporal reservoir resampling for real-time ray tracing ...
revolcs10
 
MuleSoft for AgentForce : Topic Center and API Catalog
MuleSoft for AgentForce : Topic Center and API Catalog
shyamraj55
 
Coordinated Disclosure for ML - What's Different and What's the Same.pdf
Coordinated Disclosure for ML - What's Different and What's the Same.pdf
Priyanka Aash
 
Curietech AI in action - Accelerate MuleSoft development
Curietech AI in action - Accelerate MuleSoft development
shyamraj55
 
“Key Requirements to Successfully Implement Generative AI in Edge Devices—Opt...
“Key Requirements to Successfully Implement Generative AI in Edge Devices—Opt...
Edge AI and Vision Alliance
 
From Manual to Auto Searching- FME in the Driver's Seat
From Manual to Auto Searching- FME in the Driver's Seat
Safe Software
 
GenAI Opportunities and Challenges - Where 370 Enterprises Are Focusing Now.pdf
GenAI Opportunities and Challenges - Where 370 Enterprises Are Focusing Now.pdf
Priyanka Aash
 
UserCon Belgium: Honey, VMware increased my bill
UserCon Belgium: Honey, VMware increased my bill
stijn40
 
OWASP Barcelona 2025 Threat Model Library
OWASP Barcelona 2025 Threat Model Library
PetraVukmirovic
 
You are not excused! How to avoid security blind spots on the way to production
You are not excused! How to avoid security blind spots on the way to production
Michele Leroux Bustamante
 
Cyber Defense Matrix Workshop - RSA Conference
Cyber Defense Matrix Workshop - RSA Conference
Priyanka Aash
 
FIDO Seminar: Authentication for a Billion Consumers - Amazon.pptx
FIDO Seminar: Authentication for a Billion Consumers - Amazon.pptx
FIDO Alliance
 
Securing Account Lifecycles in the Age of Deepfakes.pptx
Securing Account Lifecycles in the Age of Deepfakes.pptx
FIDO Alliance
 
OpenACC and Open Hackathons Monthly Highlights June 2025
OpenACC and Open Hackathons Monthly Highlights June 2025
OpenACC
 
cnc-processing-centers-centateq-p-110-en.pdf
cnc-processing-centers-centateq-p-110-en.pdf
AmirStern2
 
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
"How to survive Black Friday: preparing e-commerce for a peak season", Yurii ...
Fwdays
 
Connecting Data and Intelligence: The Role of FME in Machine Learning
Connecting Data and Intelligence: The Role of FME in Machine Learning
Safe Software
 
Using the SQLExecutor for Data Quality Management: aka One man's love for the...
Using the SQLExecutor for Data Quality Management: aka One man's love for the...
Safe Software
 
Information Security Response Team Nepal_npCERT_Vice_President_Sudan_Jha.pdf
Information Security Response Team Nepal_npCERT_Vice_President_Sudan_Jha.pdf
ICT Frame Magazine Pvt. Ltd.
 
PyCon SG 25 - Firecracker Made Easy with Python.pdf
PyCon SG 25 - Firecracker Made Easy with Python.pdf
Muhammad Yuga Nugraha
 
ReSTIR [DI]: Spatiotemporal reservoir resampling for real-time ray tracing ...
ReSTIR [DI]: Spatiotemporal reservoir resampling for real-time ray tracing ...
revolcs10
 
MuleSoft for AgentForce : Topic Center and API Catalog
MuleSoft for AgentForce : Topic Center and API Catalog
shyamraj55
 
Coordinated Disclosure for ML - What's Different and What's the Same.pdf
Coordinated Disclosure for ML - What's Different and What's the Same.pdf
Priyanka Aash
 
Curietech AI in action - Accelerate MuleSoft development
Curietech AI in action - Accelerate MuleSoft development
shyamraj55
 
“Key Requirements to Successfully Implement Generative AI in Edge Devices—Opt...
“Key Requirements to Successfully Implement Generative AI in Edge Devices—Opt...
Edge AI and Vision Alliance
 
From Manual to Auto Searching- FME in the Driver's Seat
From Manual to Auto Searching- FME in the Driver's Seat
Safe Software
 
GenAI Opportunities and Challenges - Where 370 Enterprises Are Focusing Now.pdf
GenAI Opportunities and Challenges - Where 370 Enterprises Are Focusing Now.pdf
Priyanka Aash
 
UserCon Belgium: Honey, VMware increased my bill
UserCon Belgium: Honey, VMware increased my bill
stijn40
 

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

  • 1. Write code that writes code! A beginner’s guide to annotation processing.
  • 2. 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.
  • 3. 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?
  • 5. –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
  • 6. 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.
  • 9. • Useless (until you actually use them…) Custom Annotations • Useless
  • 10. • Useless • Run-time - with reflection • Compile-time “Annotation Processor” • Useless (until you actually use them…) Custom Annotations • Useless
  • 11. –Everyone “Reflection is slow and you should never use it.”
  • 12. –Smart People “Reflection is slow and you should try to avoid using it on the main thread.”
  • 13. 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
  • 14. Annotation Processing List unprocessed source files with annotations. Register Annotation Processors Any Processors for them? Run ProcessorsCompile No* Yes
  • 15. 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 ProcessorsCompile No* Yes
  • 16. Why would you want to make one? • Boilerplate Reduction • Reducing Boilerplate • Reduced Boilerplate • …. • It’s pretty cool.
  • 18. “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.
  • 19. 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.
  • 20. 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.
  • 21. 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.
  • 27. 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.
  • 28. Extending AbstractProcessor public 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 } }
  • 29. Extending AbstractProcessor public 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 } }
  • 30. Extending AbstractProcessor public 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 } }
  • 31. Extending AbstractProcessor public 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 } }
  • 32. Extending AbstractProcessor public 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 } }
  • 33. Extending AbstractProcessor public 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 } }
  • 34. Extending AbstractProcessor public 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 } }
  • 35. 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.
  • 36. Processing… @Override
 public 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 .. }
  • 37. Processing… @Override
 public 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 .. }
  • 38. Processing… @Override
 public 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 .. }
  • 39. Processing… @Override
 public 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 .. }
  • 40. 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.
  • 41. @Override
 public 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);
 } }
  • 42. @Override
 public 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);
 } }
  • 43. @Override
 public 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);
 } }
  • 44. @Override
 public 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);
 } } 😱
  • 45. There has to be a better way!?
  • 46. There is a better way.
  • 49. 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.
  • 50. 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]);
 }
 }

  • 51. 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]);
 }
 }

  • 52. 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]);
 }
 }

  • 53. 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]);
 }
 }

  • 54. 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]);
 }
 }

  • 55. 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.
  • 56. @Override
 public 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; }
  • 57. @Override
 public 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; }
  • 58. @Override
 public 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; }
  • 59. @Override
 public 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; }
  • 60. @Override
 public 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; }
  • 61. @Override
 public 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; }
  • 62. @Override
 public 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)"
  • 63. @Override
 public 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)"
  • 64. @Override
 public 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)"
  • 65. @Override
 public 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)"
  • 66. @Override
 public 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; }
  • 67. @Override
 public 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; }
  • 68. @Override
 public 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; }
  • 69. @Override
 public 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; } 👀
  • 70. @Override
 public 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; }
  • 71. @Override
 public 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; }
  • 72. We’ve got an annotation processor now!
  • 73. How do we tell the build process about it?
  • 74. 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.
  • 75. 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.
  • 77. SoupLadle Module 
 apply plugin: 'java'
 
 dependencies {
 compile fileTree(dir: 'libs', include: ['*.jar'])
 compile 'com.squareup:javapoet:1.7.0'
 }
  • 80. 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.
  • 81. Project build.gradle buildscript {
 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
 }
  • 82. Project build.gradle buildscript {
 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
 }
  • 83. 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.
  • 84. App build.gradle apply 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')
 }
  • 85. App build.gradle apply 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')
 }
  • 86. App build.gradle apply 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')
 }
  • 89. 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);
 }
 }
  • 90. 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);
 }
 }
  • 92. 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);
 }
 }

  • 93. 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!");
 }
 }
  • 96. Thank you! Questions? Twitter: @jasonwyatt github.com/jasonwyatt bandcamp.com/jasonwyatt Source Code available at: github.com/jasonwyatt/Soup-Ladle