di: objects of generic types
This commit is contained in:
@@ -33,6 +33,7 @@ import java.io.PrintStream;
|
|||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,7 +45,7 @@ public class DependencyInjectorGenerator {
|
|||||||
private static final String LOCK_NAME = "__lock__";
|
private static final String LOCK_NAME = "__lock__";
|
||||||
|
|
||||||
private final boolean lazy;
|
private final boolean lazy;
|
||||||
private final Multimap<Class<?>, Field> typeToField = HashMultimap.create();
|
private final Multimap<DiType, Field> typeToField = HashMultimap.create();
|
||||||
private final Set<Field> satisfied = Sets.newHashSet();
|
private final Set<Field> satisfied = Sets.newHashSet();
|
||||||
private final Set<Field> fields = Sets.newLinkedHashSet();
|
private final Set<Field> fields = Sets.newLinkedHashSet();
|
||||||
private final Set<Parameter> parameters = Sets.newLinkedHashSet();
|
private final Set<Parameter> parameters = Sets.newLinkedHashSet();
|
||||||
@@ -119,25 +120,37 @@ public class DependencyInjectorGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addPublicParameter(Class<?> type) {
|
public void addPublicParameter(Class<?> type) {
|
||||||
|
addPublicParameter(new DiType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPublicParameter(DiType type) {
|
||||||
addPublicParameter(type, true);
|
addPublicParameter(type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPublicParameter(Class<?> type, boolean required) {
|
public void addPublicParameter(DiType type, boolean required) {
|
||||||
addParameter(true, type, var(type), required);
|
addParameter(true, type, var(type), required);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addParameter(Class<?> type) {
|
public void addParameter(Class<?> type) {
|
||||||
|
addParameter(DiType.fromReflectionType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addParameter(DiType type) {
|
||||||
addParameter(type, true);
|
addParameter(type, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addParameter(Class<?> type, boolean required) {
|
public void addParameter(Class<?> type, boolean required) {
|
||||||
|
addParameter(new DiType(type), required);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addParameter(DiType type, boolean required) {
|
||||||
addParameter(false, type, var(type), required);
|
addParameter(false, type, var(type), required);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void addParameter(boolean reexport, @NotNull Class<?> type, @Nullable String name, boolean required) {
|
public void addParameter(boolean reexport, @NotNull DiType type, @Nullable String name, boolean required) {
|
||||||
Field field = addField(reexport, type, name, null);
|
Field field = addField(reexport, type, name, null);
|
||||||
Parameter parameter = new Parameter(type, name, field, required);
|
Parameter parameter = new Parameter(type, name, field, required);
|
||||||
parameters.add(parameter);
|
parameters.add(parameter);
|
||||||
@@ -147,14 +160,26 @@ public class DependencyInjectorGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Field addPublicField(Class<?> type) {
|
public Field addPublicField(Class<?> type) {
|
||||||
|
return addPublicField(new DiType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field addPublicField(DiType type) {
|
||||||
return addField(true, type, null, null);
|
return addField(true, type, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Field addField(Class<?> type) {
|
public Field addField(Class<?> type) {
|
||||||
|
return addField(new DiType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field addField(DiType type) {
|
||||||
return addField(false, type, null, null);
|
return addField(false, type, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Field addField(boolean isPublic, Class<?> type, @Nullable String name, @Nullable Expression init) {
|
public Field addField(boolean isPublic, Class<?> type, @Nullable String name, @Nullable Expression init) {
|
||||||
|
return addField(isPublic, new DiType(type), name, init);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Field addField(boolean isPublic, DiType type, @Nullable String name, @Nullable Expression init) {
|
||||||
Field field = Field.create(isPublic, type, name == null ? var(type) : name, init);
|
Field field = Field.create(isPublic, type, name == null ? var(type) : name, init);
|
||||||
fields.add(field);
|
fields.add(field);
|
||||||
typeToField.put(type, field);
|
typeToField.put(type, field);
|
||||||
@@ -163,10 +188,10 @@ public class DependencyInjectorGenerator {
|
|||||||
|
|
||||||
private void generateImports(PrintStream out, String injectorPackageName) {
|
private void generateImports(PrintStream out, String injectorPackageName) {
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
generateImportDirective(out, field.getType(), injectorPackageName);
|
generateImportDirectives(out, field.getType(), injectorPackageName);
|
||||||
}
|
}
|
||||||
for (Parameter parameter : parameters) {
|
for (Parameter parameter : parameters) {
|
||||||
generateImportDirective(out, parameter.getType(), injectorPackageName);
|
generateImportDirectives(out, parameter.getType(), injectorPackageName);
|
||||||
}
|
}
|
||||||
for (Parameter parameter : parameters) {
|
for (Parameter parameter : parameters) {
|
||||||
if (parameter.isRequired()) {
|
if (parameter.isRequired()) {
|
||||||
@@ -176,6 +201,13 @@ public class DependencyInjectorGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateImportDirectives(PrintStream out, DiType type, String injectorPackageName) {
|
||||||
|
generateImportDirective(out, type.getClazz(), injectorPackageName);
|
||||||
|
for (DiType typeParameter : type.getTypeParameters()) {
|
||||||
|
generateImportDirectives(out, typeParameter, injectorPackageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void generateImportDirective(PrintStream out, Class<?> type, String injectorPackageName) {
|
private void generateImportDirective(PrintStream out, Class<?> type, String injectorPackageName) {
|
||||||
if (type.isPrimitive()) return;
|
if (type.isPrimitive()) return;
|
||||||
String importedPackageName = type.getPackage().getName();
|
String importedPackageName = type.getPackage().getName();
|
||||||
@@ -248,7 +280,7 @@ public class DependencyInjectorGenerator {
|
|||||||
// call @PostConstruct
|
// call @PostConstruct
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
// TODO: type of field may be different from type of object
|
// TODO: type of field may be different from type of object
|
||||||
List<Method> postConstructMethods = getPostConstructMethods(field.getType());
|
List<Method> postConstructMethods = getPostConstructMethods(field.getType().getClazz());
|
||||||
for (Method postConstruct : postConstructMethods) {
|
for (Method postConstruct : postConstructMethods) {
|
||||||
out.println(indent + field.getName() + "." + postConstruct.getName() + "();");
|
out.println(indent + field.getName() + "." + postConstruct.getName() + "();");
|
||||||
}
|
}
|
||||||
@@ -333,7 +365,7 @@ public class DependencyInjectorGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort setters in order to get deterministic behavior
|
// Sort setters in order to get deterministic behavior
|
||||||
List<Method> declaredMethods = Lists.newArrayList(field.getType().getDeclaredMethods());
|
List<Method> declaredMethods = Lists.newArrayList(field.getType().getClazz().getDeclaredMethods());
|
||||||
Collections.sort(declaredMethods, new Comparator<Method>() {
|
Collections.sort(declaredMethods, new Comparator<Method>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(Method o1, Method o2) {
|
public int compare(Method o1, Method o2) {
|
||||||
@@ -345,18 +377,18 @@ public class DependencyInjectorGenerator {
|
|||||||
|| !method.getName().startsWith("set")
|
|| !method.getName().startsWith("set")
|
||||||
|| method.getParameterTypes().length != 1) continue;
|
|| method.getParameterTypes().length != 1) continue;
|
||||||
|
|
||||||
Class<?> parameterType = method.getParameterTypes()[0];
|
Type parameterType = method.getGenericParameterTypes()[0];
|
||||||
|
|
||||||
|
|
||||||
Field dependency = findDependencyOfType(parameterType, field + ": " + method + ": " + fields, field);
|
Field dependency = findDependencyOfType(DiType.fromReflectionType(parameterType), field + ": " + method + ": " + fields, field);
|
||||||
|
|
||||||
field.getDependencies().add(new SetterDependency(field, method.getName(), dependency));
|
field.getDependencies().add(new SetterDependency(field, method.getName(), dependency));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Field findDependencyOfType(Class<?> parameterType, String errorMessage, Field neededFor) {
|
private Field findDependencyOfType(DiType parameterType, String errorMessage, Field neededFor) {
|
||||||
List<Field> fields = Lists.newArrayList();
|
List<Field> fields = Lists.newArrayList();
|
||||||
for (Map.Entry<Class<?>, Field> entry : typeToField.entries()) {
|
for (Map.Entry<DiType, Field> entry : typeToField.entries()) {
|
||||||
if (parameterType.isAssignableFrom(entry.getKey())) {
|
if (parameterType.isAssignableFrom(entry.getKey())) {
|
||||||
fields.add(entry.getValue());
|
fields.add(entry.getValue());
|
||||||
}
|
}
|
||||||
@@ -365,7 +397,7 @@ public class DependencyInjectorGenerator {
|
|||||||
Field dependency;
|
Field dependency;
|
||||||
if (fields.isEmpty()) {
|
if (fields.isEmpty()) {
|
||||||
|
|
||||||
if (parameterType.isPrimitive() || parameterType.getPackage().getName().equals("java.lang")) {
|
if (parameterType.getClazz().isPrimitive() || parameterType.getClazz().getPackage().getName().equals("java.lang")) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"cannot declare magic field of type " + parameterType + ": " + errorMessage);
|
"cannot declare magic field of type " + parameterType + ": " + errorMessage);
|
||||||
}
|
}
|
||||||
@@ -383,35 +415,45 @@ public class DependencyInjectorGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeByConstructorCall(Field field, Field neededFor) {
|
private void initializeByConstructorCall(Field field, Field neededFor) {
|
||||||
Class<?> type = field.getType();
|
DiType type = field.getType();
|
||||||
|
|
||||||
if (type.isInterface()) {
|
if (type.getClazz().isInterface()) {
|
||||||
throw new IllegalArgumentException("cannot instantiate interface: " + type.getName() + " needed for " + neededFor);
|
throw new IllegalArgumentException("cannot instantiate interface: " + type.getClazz().getName() + " needed for " + neededFor);
|
||||||
}
|
}
|
||||||
if (Modifier.isAbstract(type.getModifiers())) {
|
if (Modifier.isAbstract(type.getClazz().getModifiers())) {
|
||||||
throw new IllegalArgumentException("cannot instantiate abstract class: " + type.getName() + " needed for " + neededFor);
|
throw new IllegalArgumentException("cannot instantiate abstract class: " + type.getClazz().getName() + " needed for " + neededFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: projections are not computed here
|
||||||
|
|
||||||
// Look for constructor
|
// Look for constructor
|
||||||
Constructor<?>[] constructors = type.getConstructors();
|
Constructor<?>[] constructors = type.getClazz().getConstructors();
|
||||||
if (constructors.length == 0 || !Modifier.isPublic(constructors[0].getModifiers())) {
|
if (constructors.length == 0 || !Modifier.isPublic(constructors[0].getModifiers())) {
|
||||||
throw new IllegalArgumentException("No constructor: " + type.getName() + " needed for " + neededFor);
|
throw new IllegalArgumentException("No constructor: " + type.getClazz().getName() + " needed for " + neededFor);
|
||||||
}
|
}
|
||||||
Constructor<?> constructor = constructors[0];
|
Constructor<?> constructor = constructors[0];
|
||||||
|
|
||||||
// Find arguments
|
// Find arguments
|
||||||
ConstructorCall dependency = new ConstructorCall(constructor);
|
ConstructorCall dependency = new ConstructorCall(constructor);
|
||||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
Type[] parameterTypes = constructor.getGenericParameterTypes();
|
||||||
for (Class<?> parameterType : parameterTypes) {
|
for (Type parameterType : parameterTypes) {
|
||||||
Field fieldForParameter = findDependencyOfType(parameterType, "constructor: " + constructor + ", parameter: " + parameterType, field);
|
Field fieldForParameter = findDependencyOfType(DiType.fromReflectionType(parameterType), "constructor: " + constructor + ", parameter: " + parameterType, field);
|
||||||
dependency.getConstructorArguments().add(fieldForParameter);
|
dependency.getConstructorArguments().add(fieldForParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
field.setInitialization(dependency);
|
field.setInitialization(dependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String var(Class<?> theClass) {
|
private String var(@NotNull DiType type) {
|
||||||
return StringUtil.decapitalize(theClass.getSimpleName());
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(StringUtil.decapitalize(type.getClazz().getSimpleName()));
|
||||||
|
if (type.getTypeParameters().size() > 0) {
|
||||||
|
sb.append("Of");
|
||||||
|
}
|
||||||
|
for (DiType parameter : type.getTypeParameters()) {
|
||||||
|
sb.append(StringUtil.capitalize(var(parameter)));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generateMakeFunction(PrintStream out) {
|
private void generateMakeFunction(PrintStream out) {
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2012 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.jet.di;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Stepan Koltsov
|
||||||
|
*/
|
||||||
|
class DiType {
|
||||||
|
@NotNull
|
||||||
|
private final Class<?> clazz;
|
||||||
|
@NotNull
|
||||||
|
private final List<DiType> typeParameters;
|
||||||
|
|
||||||
|
public DiType(@NotNull Class<?> clazz, @NotNull List<DiType> typeParameters) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.typeParameters = typeParameters;
|
||||||
|
|
||||||
|
if (clazz.getTypeParameters().length != typeParameters.size()) {
|
||||||
|
throw new IllegalStateException("type parameter count mismatch: " + clazz + ", " + typeParameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiType(@NotNull Class<?> clazz) {
|
||||||
|
this(clazz, Collections.<DiType>emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Class<?> getClazz() {
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public List<DiType> getTypeParameters() {
|
||||||
|
return typeParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAssignableFrom(@NotNull DiType that) {
|
||||||
|
if (!this.clazz.isAssignableFrom(that.clazz)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: following code incorrectly assumes that each type parameter
|
||||||
|
// is projected into type parameter in the same position
|
||||||
|
return this.typeParameters.equals(that.typeParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getSimpleName() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(clazz.getSimpleName());
|
||||||
|
Iterator<DiType> typeParameterIterator = typeParameters.iterator();
|
||||||
|
if (typeParameterIterator.hasNext()) {
|
||||||
|
sb.append("<");
|
||||||
|
if (typeParameterIterator.hasNext()) {
|
||||||
|
sb.append(typeParameterIterator.next().getSimpleName());
|
||||||
|
}
|
||||||
|
while (typeParameterIterator.hasNext()) {
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(typeParameterIterator.next().getSimpleName());
|
||||||
|
}
|
||||||
|
sb.append(">");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
DiType type = (DiType) o;
|
||||||
|
|
||||||
|
if (!clazz.equals(type.clazz)) return false;
|
||||||
|
if (!typeParameters.equals(type.typeParameters)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = clazz.hashCode();
|
||||||
|
result = 31 * result + typeParameters.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static DiType fromReflectionType(@NotNull Type type) {
|
||||||
|
if (type instanceof Class<?>) {
|
||||||
|
return new DiType((Class) type);
|
||||||
|
}
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||||
|
Class<?> owner = (Class<?>) parameterizedType.getRawType();
|
||||||
|
List<DiType> diTypeParameters = Lists.newArrayList();
|
||||||
|
for (Type typeParameter : parameterizedType.getActualTypeArguments()) {
|
||||||
|
diTypeParameters.add(fromReflectionType(typeParameter));
|
||||||
|
}
|
||||||
|
return new DiType(owner, diTypeParameters);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("unsupported type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,13 +28,13 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
class Field {
|
class Field {
|
||||||
|
|
||||||
public static Field create(boolean isPublic, Class<?> type, String name, @Nullable Expression init) {
|
public static Field create(boolean isPublic, DiType type, String name, @Nullable Expression init) {
|
||||||
Field field = new Field(isPublic, type, name);
|
Field field = new Field(isPublic, type, name);
|
||||||
field.initialization = init;
|
field.initialization = init;
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Class<?> type;
|
private final DiType type;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final boolean isPublic;
|
private final boolean isPublic;
|
||||||
|
|
||||||
@@ -43,13 +43,13 @@ class Field {
|
|||||||
|
|
||||||
private final List<SetterDependency> dependencies = Lists.newArrayList();
|
private final List<SetterDependency> dependencies = Lists.newArrayList();
|
||||||
|
|
||||||
Field(boolean isPublic, Class<?> type, String name) {
|
Field(boolean isPublic, DiType type, String name) {
|
||||||
this.isPublic = isPublic;
|
this.isPublic = isPublic;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getType() {
|
public DiType getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,12 +58,12 @@ class Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getTypeName() {
|
public String getTypeName() {
|
||||||
return type.getSimpleName();
|
return type.getClazz().getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGetterName() {
|
public String getGetterName() {
|
||||||
String prefix;
|
String prefix;
|
||||||
if (getType() == boolean.class || getType() == Boolean.class) {
|
if (getType().getClazz() == boolean.class || getType().getClazz() == Boolean.class) {
|
||||||
prefix = "is";
|
prefix = "is";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -20,19 +20,19 @@ package org.jetbrains.jet.di;
|
|||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
class Parameter {
|
class Parameter {
|
||||||
private final Class<?> type;
|
private final DiType type;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Field field;
|
private final Field field;
|
||||||
private final boolean required;
|
private final boolean required;
|
||||||
|
|
||||||
Parameter(Class<?> type, String name, Field field, boolean required) {
|
Parameter(DiType type, String name, Field field, boolean required) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.required = required;
|
this.required = required;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getType() {
|
public DiType getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user