Singletons supported in Injector Generator

This commit is contained in:
Andrey Breslav
2014-12-02 17:53:14 +03:00
parent 6efa0032e5
commit 0bc721fd77
2 changed files with 79 additions and 32 deletions
@@ -128,36 +128,37 @@ public class Dependencies {
Class<?> clazz = type.getClazz(); Class<?> clazz = type.getClazz();
if (clazz.isInterface()) { if (clazz.isInterface()) {
if (initializeAsSingleton(field, type)) return;
throw new IllegalArgumentException("cannot instantiate interface: " + clazz.getName() + " needed for " + neededFor); throw new IllegalArgumentException("cannot instantiate interface: " + clazz.getName() + " needed for " + neededFor);
} }
if (Modifier.isAbstract(clazz.getModifiers())) { if (Modifier.isAbstract(clazz.getModifiers())) {
if (initializeAsSingleton(field, type)) return;
throw new IllegalArgumentException("cannot instantiate abstract class: " + clazz.getName() + " needed for " + neededFor); throw new IllegalArgumentException("cannot instantiate abstract class: " + clazz.getName() + " needed for " + neededFor);
} }
// Note: projections are not computed here // Note: projections are not computed here
if (isKotlinSingletonObject(clazz)) { // Look for constructor
field.setInitialization(new ObjectInstanceFieldAccess(clazz)); List<Constructor<?>> publicConstructors = findPublicConstructors(clazz.getConstructors());
return; if (publicConstructors.size() != 1) {
if (initializeAsSingleton(field, type)) return;
if (publicConstructors.size() == 0) {
throw new IllegalArgumentException("No public constructor: " + clazz.getName() + " needed for " + neededFor);
}
else {
throw new IllegalArgumentException("Too many public constructors in " + clazz.getName() + " needed for " + neededFor);
}
} }
// Look for constructor Constructor<?> publicConstructor = publicConstructors.get(0);
Constructor<?>[] constructors = clazz.getConstructors();
if (constructors.length == 0 || !Modifier.isPublic(constructors[0].getModifiers())) {
throw new IllegalArgumentException("No constructor: " + clazz.getName() + " needed for " + neededFor);
}
if (constructors.length > 1) {
throw new IllegalArgumentException("Too many constructors in " + clazz.getName() + " needed for " + neededFor);
}
Constructor<?> constructor = constructors[0];
// Find arguments // Find arguments
ConstructorCall dependency = new ConstructorCall(constructor); ConstructorCall dependency = new ConstructorCall(publicConstructor);
Type[] parameterTypes = constructor.getGenericParameterTypes(); Type[] parameterTypes = publicConstructor.getGenericParameterTypes();
for (Type parameterType : parameterTypes) { for (Type parameterType : parameterTypes) {
Field fieldForParameter = findDependencyOfType( Field fieldForParameter = findDependencyOfType(
DiType.fromReflectionType(parameterType), DiType.fromReflectionType(parameterType),
"constructor: " + constructor + ", parameter: " + parameterType, "constructor: " + publicConstructor + ", parameter: " + parameterType,
neededFor.prepend(field) neededFor.prepend(field)
); );
used.add(fieldForParameter); used.add(fieldForParameter);
@@ -167,14 +168,48 @@ public class Dependencies {
field.setInitialization(dependency); field.setInitialization(dependency);
} }
private static boolean isKotlinSingletonObject(Class<?> clazz) { @NotNull
private static List<Constructor<?>> findPublicConstructors(Constructor<?>[] constructors) {
List<Constructor<?>> result = new ArrayList<Constructor<?>>();
for (Constructor<?> constructor : constructors) {
if (Modifier.isPublic(constructor.getModifiers())) {
result.add(constructor);
}
}
return result;
}
private static boolean initializeAsSingleton(Field field, DiType type) {
Class<?> clazz = type.getClazz();
return initializeBySingletonMethod(field, clazz, "getInstance")
|| initializeBySingletonField(field, clazz, "INSTANCE")
|| initializeBySingletonField(field, clazz, "INSTANCE$");
}
private static boolean initializeBySingletonMethod(Field field, Class<?> clazz, String name) {
try { try {
clazz.getDeclaredField("INSTANCE$"); clazz.getMethod(name);
field.setInitialization(GetSingleton.byMethod(clazz, name));
return true;
}
catch (NoSuchMethodException e) {
// Ignored
}
return false;
}
private static boolean initializeBySingletonField(Field field, Class<?> clazz, String name) {
try {
clazz.getField(name);
field.setInitialization(GetSingleton.byField(clazz, name));
return true; return true;
} }
catch (NoSuchFieldException e) { catch (NoSuchFieldException e) {
return false; // Ignored
} }
return false;
} }
public Collection<Field> satisfyDependencies() { public Collection<Field> satisfyDependencies() {
@@ -17,38 +17,50 @@
package org.jetbrains.jet.di; package org.jetbrains.jet.di;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
public class ObjectInstanceFieldAccess implements Expression { public class GetSingleton implements Expression {
private final Class<?> clazz;
public ObjectInstanceFieldAccess(@NotNull Class<?> clazz) { public static GetSingleton byMethod(@NotNull Class<?> singletonClass, @NotNull String methodName) {
this.clazz = clazz; return new GetSingleton(singletonClass, methodName, "()");
}
public static GetSingleton byField(@NotNull Class<?> singletonClass, @NotNull String fieldName) {
return new GetSingleton(singletonClass, fieldName, "");
}
private final Class<?> singletonClass;
private final String memberName;
private final String callSuffix;
private GetSingleton(@NotNull Class<?> singletonClass, @NotNull String name, String callSuffix) {
this.singletonClass = singletonClass;
this.memberName = name;
this.callSuffix = callSuffix;
}
@Override
public String toString() {
return renderAsCode();
} }
@NotNull @NotNull
@Override @Override
public String renderAsCode() { public String renderAsCode() {
return clazz.getSimpleName() + ".INSTANCE$"; return singletonClass.getSimpleName() + "." + memberName + callSuffix;
} }
@NotNull @NotNull
@Override @Override
public Collection<DiType> getTypesToImport() { public Collection<DiType> getTypesToImport() {
return Collections.singleton(new DiType(clazz)); return Collections.singletonList(new DiType(singletonClass));
} }
@Nullable @NotNull
@Override @Override
public DiType getType() { public DiType getType() {
return new DiType(clazz); return new DiType(singletonClass);
}
@Override
public String toString() {
return clazz.toString();
} }
} }