Skip to content

Commit

Permalink
Merge pull request square#37 from tbroyer/staticinjection-codegen
Browse files Browse the repository at this point in the history
Code generation for static injection.
  • Loading branch information
swankjesse committed Oct 3, 2012
2 parents 3945d2d + ec17019 commit 979c9d3
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 29 deletions.
71 changes: 48 additions & 23 deletions core/src/main/java/dagger/internal/StaticInjection.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,42 @@
*/
package dagger.internal;

import dagger.ObjectGraph;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;

/**
* Injects the static fields of a class.
*
* @author Jesse Wilson
*/
public final class StaticInjection {
private final Field[] fields;
private Binding<?>[] bindings;
public abstract class StaticInjection {
private static final Logger LOGGER = Logger.getLogger(ObjectGraph.class.getName());

private StaticInjection(Field[] fields) {
this.fields = fields;
}
public abstract void attach(Linker linker);

public abstract void inject();

public static StaticInjection get(Class<?> injectedClass) {
try {
String adapter = injectedClass.getName() + "$StaticInjection";
Class<?> c = Class.forName(adapter);
Constructor<?> constructor = c.getConstructor();
constructor.setAccessible(true);
return (StaticInjection) constructor.newInstance();
} catch (Exception e) {
LOGGER.log(Level.FINE, "No generated static injection for " + injectedClass.getName()
+ ". Falling back to reflection.", e);
}

public static StaticInjection get(Class<?> c) {
List<Field> fields = new ArrayList<Field>();
for (Field field : c.getDeclaredFields()) {
for (Field field : injectedClass.getDeclaredFields()) {
if (field.getAnnotation(Inject.class) == null
|| !Modifier.isStatic(field.getModifiers())) {
continue;
Expand All @@ -45,27 +59,38 @@ public static StaticInjection get(Class<?> c) {
fields.add(field);
}
if (fields.isEmpty()) {
throw new IllegalArgumentException("No static injections: " + c.getName());
throw new IllegalArgumentException("No static injections: " + injectedClass.getName());
}
return new StaticInjection(fields.toArray(new Field[fields.size()]));
return new ReflectiveStaticInjection(fields.toArray(new Field[fields.size()]));
}

public void attach(Linker linker) {
bindings = new Binding<?>[fields.length];
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
String key = Keys.get(field.getGenericType(), field.getAnnotations(), field);
bindings[i] = linker.requestBinding(key, field);
static class ReflectiveStaticInjection extends StaticInjection {
private final Field[] fields;
private Binding<?>[] bindings;

private ReflectiveStaticInjection(Field[] fields) {
this.fields = fields;
}
}

public void inject() {
try {
for (int f = 0; f < fields.length; f++) {
fields[f].set(null, bindings[f].get());
@Override
public void attach(Linker linker) {
bindings = new Binding<?>[fields.length];
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
String key = Keys.get(field.getGenericType(), field.getAnnotations(), field);
bindings[i] = linker.requestBinding(key, field);
}
}

@Override
public void inject() {
try {
for (int f = 0; f < fields.length; f++) {
fields[f].set(null, bindings[f].get());
}
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}
}
73 changes: 67 additions & 6 deletions core/src/main/java/dagger/internal/codegen/InjectProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import dagger.internal.Binding;
import dagger.internal.Linker;
import dagger.internal.StaticInjection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -53,7 +54,12 @@ public final class InjectProcessor extends AbstractProcessor {
@Override public boolean process(Set<? extends TypeElement> types, RoundEnvironment env) {
try {
for (InjectedClass injectedClass : getInjectedClasses(env)) {
writeInjectAdapter(injectedClass.type, injectedClass.constructor, injectedClass.fields);
if (injectedClass.constructor != null || !injectedClass.fields.isEmpty()) {
writeInjectAdapter(injectedClass.type, injectedClass.constructor, injectedClass.fields);
}
if (!injectedClass.staticFields.isEmpty()) {
writeStaticInjection(injectedClass.type, injectedClass.staticFields);
}
}
} catch (IOException e) {
error("Code gen failed: %s", e);
Expand Down Expand Up @@ -82,17 +88,21 @@ private Set<InjectedClass> getInjectedClasses(RoundEnvironment env) {
*/
private InjectedClass getInjectedClass(TypeElement type) {
boolean isAbstract = type.getModifiers().contains(Modifier.ABSTRACT);
List<Element> staticFields = new ArrayList<Element>();
ExecutableElement constructor = null;
List<Element> fields = new ArrayList<Element>();
for (Element member : type.getEnclosedElements()) {
if (member.getAnnotation(Inject.class) == null
|| member.getModifiers().contains(Modifier.STATIC)) {
if (member.getAnnotation(Inject.class) == null) {
continue;
}

switch (member.getKind()) {
case FIELD:
fields.add(member);
if (member.getModifiers().contains(Modifier.STATIC)) {
staticFields.add(member);
} else {
fields.add(member);
}
break;
case CONSTRUCTOR:
if (constructor != null) {
Expand All @@ -113,7 +123,7 @@ private InjectedClass getInjectedClass(TypeElement type) {
constructor = findNoArgsConstructor(type);
}

return new InjectedClass(type, constructor, fields);
return new InjectedClass(type, staticFields, constructor, fields);
}

/**
Expand Down Expand Up @@ -270,6 +280,55 @@ private void writeInjectAdapter(TypeElement type, ExecutableElement constructor,
writer.close();
}

/**
* Write a companion class for {@code type} that extends {@link StaticInjection}.
*/
private void writeStaticInjection(TypeElement type, List<Element> fields) throws IOException {
String typeName = type.getQualifiedName().toString();
String adapterName = CodeGen.adapterName(type, "$StaticInjection");
JavaFileObject sourceFile = processingEnv.getFiler()
.createSourceFile(adapterName, type);
JavaWriter writer = new JavaWriter(sourceFile.openWriter());

writer.addPackage(CodeGen.getPackage(type).getQualifiedName().toString());
writer.addImport(StaticInjection.class);
writer.addImport(Binding.class);
writer.addImport(Linker.class);

writer.beginType(adapterName, "class", PUBLIC | FINAL, StaticInjection.class.getName());

for (int f = 0; f < fields.size(); f++) {
TypeMirror fieldType = fields.get(f).asType();
writer.field(CodeGen.parameterizedType(Binding.class, CodeGen.typeToString(fieldType)),
fieldName(f), PRIVATE);
}

writer.annotation(Override.class);
writer.beginMethod("void", "attach", PUBLIC, Linker.class.getName(), "linker");
for (int f = 0; f < fields.size(); f++) {
TypeMirror fieldType = fields.get(f).asType();
writer.statement("%s = (%s) linker.requestBinding(%s, %s.class)",
fieldName(f),
CodeGen.parameterizedType(Binding.class, CodeGen.typeToString(fieldType)),
JavaWriter.stringLiteral(GeneratorKeys.get((VariableElement) fields.get(f))),
typeName);
}
writer.endMethod();

writer.annotation(Override.class);
writer.beginMethod("void", "inject", PUBLIC);
for (int f = 0; f < fields.size(); f++) {
writer.statement("%s.%s = %s.get()",
typeName,
fields.get(f).getSimpleName().toString(),
fieldName(f));
}
writer.endMethod();

writer.endType();
writer.close();
}

private String fieldName(int index) {
return "f" + index;
}
Expand All @@ -280,11 +339,13 @@ private String constructorParameterName(int index) {

static class InjectedClass {
final TypeElement type;
final List<Element> staticFields;
final ExecutableElement constructor;
final List<Element> fields;

InjectedClass(TypeElement type, ExecutableElement constructor, List<Element> fields) {
InjectedClass(TypeElement type, List<Element> staticFields, ExecutableElement constructor, List<Element> fields) {
this.type = type;
this.staticFields = staticFields;
this.constructor = constructor;
this.fields = fields;
}
Expand Down

0 comments on commit 979c9d3

Please sign in to comment.