Dependency injection frameworks improvements
* Injectors can now implement interfaces (this was the primary goal) * Fields can have different types than getters * Imports are more accurate and no duplicate imports are added
This commit is contained in:
@@ -17,15 +17,18 @@
|
|||||||
package org.jetbrains.jet.di;
|
package org.jetbrains.jet.di;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
class ConstructorCall implements Expression {
|
public class ConstructorCall implements Expression {
|
||||||
private final Constructor<?> constructor;
|
private final Constructor<?> constructor;
|
||||||
private final List<Field> constructorArguments = Lists.newArrayList();
|
private final List<Field> constructorArguments = Lists.newArrayList();
|
||||||
|
|
||||||
@@ -39,6 +42,12 @@ class ConstructorCall implements Expression {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
return constructor.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String renderAsCode() {
|
||||||
StringBuilder builder = new StringBuilder("new " + constructor.getDeclaringClass().getSimpleName() + "(");
|
StringBuilder builder = new StringBuilder("new " + constructor.getDeclaringClass().getSimpleName() + "(");
|
||||||
for (Iterator<Field> iterator = constructorArguments.iterator(); iterator.hasNext(); ) {
|
for (Iterator<Field> iterator = constructorArguments.iterator(); iterator.hasNext(); ) {
|
||||||
Field argument = iterator.next();
|
Field argument = iterator.next();
|
||||||
@@ -55,4 +64,15 @@ class ConstructorCall implements Expression {
|
|||||||
builder.append(")");
|
builder.append(")");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<DiType> getTypesToImport() {
|
||||||
|
return Collections.singletonList(new DiType(constructor.getDeclaringClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public DiType getType() {
|
||||||
|
return new DiType(constructor.getDeclaringClass());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ public class DependencyInjectorGenerator {
|
|||||||
|
|
||||||
private final Set<Field> backsParameter = Sets.newHashSet();
|
private final Set<Field> backsParameter = Sets.newHashSet();
|
||||||
|
|
||||||
|
private final List<Class<?>> implementsList = Lists.newArrayList();
|
||||||
|
private final Collection<Class<?>> imports = Sets.newHashSet();
|
||||||
|
|
||||||
public DependencyInjectorGenerator(boolean lazy) {
|
public DependencyInjectorGenerator(boolean lazy) {
|
||||||
this.lazy = lazy;
|
this.lazy = lazy;
|
||||||
}
|
}
|
||||||
@@ -99,7 +102,9 @@ public class DependencyInjectorGenerator {
|
|||||||
out.println();
|
out.println();
|
||||||
|
|
||||||
out.println("/* This file is generated by " + AllInjectorsGenerator.class.getName() + ". DO NOT EDIT! */");
|
out.println("/* This file is generated by " + AllInjectorsGenerator.class.getName() + ". DO NOT EDIT! */");
|
||||||
out.println("public class " + injectorClassName + " {");
|
out.print("public class " + injectorClassName);
|
||||||
|
generateImplementsList(out);
|
||||||
|
out.println(" {");
|
||||||
// Needed for double-checked locking
|
// Needed for double-checked locking
|
||||||
// out.println();
|
// out.println();
|
||||||
// out.println(" private static final Object " + LOCK_NAME + " = new Object();");
|
// out.println(" private static final Object " + LOCK_NAME + " = new Object();");
|
||||||
@@ -131,6 +136,26 @@ public class DependencyInjectorGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void generateImplementsList(PrintStream out) {
|
||||||
|
if (!implementsList.isEmpty()) {
|
||||||
|
out.print(" implements ");
|
||||||
|
for (Iterator<Class<?>> iterator = implementsList.iterator(); iterator.hasNext(); ) {
|
||||||
|
Class<?> superInterface = iterator.next();
|
||||||
|
if (!superInterface.isInterface()) {
|
||||||
|
throw new IllegalArgumentException("Only interfaces are supported as supertypes");
|
||||||
|
}
|
||||||
|
out.print(superInterface.getSimpleName());
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
out.print(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void implementInterface(Class<?> superInterface) {
|
||||||
|
implementsList.add(superInterface);
|
||||||
|
}
|
||||||
|
|
||||||
public void addPublicParameter(Class<?> type) {
|
public void addPublicParameter(Class<?> type) {
|
||||||
addPublicParameter(new DiType(type));
|
addPublicParameter(new DiType(type));
|
||||||
}
|
}
|
||||||
@@ -206,6 +231,17 @@ 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) {
|
||||||
generateImportDirectives(out, field.getType(), injectorPackageName);
|
generateImportDirectives(out, field.getType(), injectorPackageName);
|
||||||
|
|
||||||
|
DiType implType = field.getInitialization().getType();
|
||||||
|
if (implType != null) {
|
||||||
|
generateImportDirectives(out, implType, injectorPackageName);
|
||||||
|
}
|
||||||
|
for (DiType typeToImport : field.getInitialization().getTypesToImport()) {
|
||||||
|
generateImportDirective(out, typeToImport.getClazz(), injectorPackageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Class<?> superInterface : implementsList) {
|
||||||
|
generateImportDirective(out, superInterface, injectorPackageName);
|
||||||
}
|
}
|
||||||
for (Parameter parameter : parameters) {
|
for (Parameter parameter : parameters) {
|
||||||
generateImportDirectives(out, parameter.getType(), injectorPackageName);
|
generateImportDirectives(out, parameter.getType(), injectorPackageName);
|
||||||
@@ -230,7 +266,8 @@ public class DependencyInjectorGenerator {
|
|||||||
if (type.isPrimitive()) return;
|
if (type.isPrimitive()) return;
|
||||||
String importedPackageName = type.getPackage().getName();
|
String importedPackageName = type.getPackage().getName();
|
||||||
if ("java.lang".equals(importedPackageName)
|
if ("java.lang".equals(importedPackageName)
|
||||||
|| injectorPackageName.equals(importedPackageName)) {
|
|| injectorPackageName.equals(importedPackageName)
|
||||||
|
|| !imports.add(type)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
out.println("import " + type.getCanonicalName() + ";");
|
out.println("import " + type.getCanonicalName() + ";");
|
||||||
@@ -239,10 +276,15 @@ public class DependencyInjectorGenerator {
|
|||||||
private void generateFields(PrintStream out) {
|
private void generateFields(PrintStream out) {
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
String _final = backsParameter.contains(field) ? "final " : "";
|
String _final = backsParameter.contains(field) ? "final " : "";
|
||||||
out.println(" private " + _final + field.getType().getSimpleName() + " " + field.getName() + ";");
|
out.println(" private " + _final + getEffectiveFieldType(field).getSimpleName() + " " + field.getName() + ";");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DiType getEffectiveFieldType(Field field) {
|
||||||
|
DiType implType = field.getInitialization().getType();
|
||||||
|
return implType == null ? field.getType() : implType;
|
||||||
|
}
|
||||||
|
|
||||||
private void generateConstructor(String injectorClassName, PrintStream out) {
|
private void generateConstructor(String injectorClassName, PrintStream out) {
|
||||||
String indent = " ";
|
String indent = " ";
|
||||||
|
|
||||||
@@ -277,7 +319,7 @@ public class DependencyInjectorGenerator {
|
|||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
//if (!backsParameter.contains(field) || field.isPublic()) {
|
//if (!backsParameter.contains(field) || field.isPublic()) {
|
||||||
String prefix = "this.";
|
String prefix = "this.";
|
||||||
out.println(indent + prefix + field.getName() + " = " + field.getInitialization() + ";");
|
out.println(indent + prefix + field.getName() + " = " + field.getInitialization().renderAsCode() + ";");
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
out.println();
|
out.println();
|
||||||
@@ -296,7 +338,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().getClazz());
|
List<Method> postConstructMethods = getPostConstructMethods(getEffectiveFieldType(field).getClazz());
|
||||||
for (Method postConstruct : postConstructMethods) {
|
for (Method postConstruct : postConstructMethods) {
|
||||||
out.println(indent + field.getName() + "." + postConstruct.getName() + "();");
|
out.println(indent + field.getName() + "." + postConstruct.getName() + "();");
|
||||||
}
|
}
|
||||||
@@ -335,7 +377,7 @@ public class DependencyInjectorGenerator {
|
|||||||
out.println(" public void destroy() {");
|
out.println(" public void destroy() {");
|
||||||
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> preDestroyMethods = getPreDestroyMethods(field.getType().getClazz());
|
List<Method> preDestroyMethods = getPreDestroyMethods(getEffectiveFieldType(field).getClazz());
|
||||||
for (Method preDestroy : preDestroyMethods) {
|
for (Method preDestroy : preDestroyMethods) {
|
||||||
out.println(" " + field.getName() + "." + preDestroy.getName() + "();");
|
out.println(" " + field.getName() + "." + preDestroy.getName() + "();");
|
||||||
}
|
}
|
||||||
@@ -359,7 +401,7 @@ public class DependencyInjectorGenerator {
|
|||||||
|
|
||||||
if (lazy && !backsParameter.contains(field)) {
|
if (lazy && !backsParameter.contains(field)) {
|
||||||
Expression initialization = field.getInitialization();
|
Expression initialization = field.getInitialization();
|
||||||
assert initialization != null : field;
|
assert initialization instanceof InstantiateType : field;
|
||||||
|
|
||||||
// Double-checked locking
|
// Double-checked locking
|
||||||
out.println(indent1 + "if (this." + field.getName() + " == null) {");
|
out.println(indent1 + "if (this." + field.getName() + " == null) {");
|
||||||
@@ -400,12 +442,14 @@ public class DependencyInjectorGenerator {
|
|||||||
if (!satisfied.add(field)) return;
|
if (!satisfied.add(field)) return;
|
||||||
if (backsParameter.contains(field)) return;
|
if (backsParameter.contains(field)) return;
|
||||||
|
|
||||||
if (field.getInitialization() == null) {
|
Expression initialization = field.getInitialization();
|
||||||
|
if (initialization instanceof InstantiateType) {
|
||||||
initializeByConstructorCall(field, neededFor);
|
initializeByConstructorCall(field, neededFor);
|
||||||
}
|
}
|
||||||
|
DiType typeToInitialize = getEffectiveFieldType(field);
|
||||||
|
|
||||||
// Sort setters in order to get deterministic behavior
|
// Sort setters in order to get deterministic behavior
|
||||||
List<Method> declaredMethods = Lists.newArrayList(field.getType().getClazz().getDeclaredMethods());
|
List<Method> declaredMethods = Lists.newArrayList(typeToInitialize.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) {
|
||||||
@@ -455,7 +499,8 @@ public class DependencyInjectorGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeByConstructorCall(Field field, Field neededFor) {
|
private void initializeByConstructorCall(Field field, Field neededFor) {
|
||||||
DiType type = field.getType();
|
Expression initialization = field.getInitialization();
|
||||||
|
DiType type = ((InstantiateType) initialization).getType();
|
||||||
|
|
||||||
if (type.getClazz().isInterface()) {
|
if (type.getClazz().isInterface()) {
|
||||||
throw new IllegalArgumentException("cannot instantiate interface: " + type.getClazz().getName() + " needed for " + neededFor);
|
throw new IllegalArgumentException("cannot instantiate interface: " + type.getClazz().getName() + " needed for " + neededFor);
|
||||||
|
|||||||
@@ -16,7 +16,21 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.di;
|
package org.jetbrains.jet.di;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
interface Expression {}
|
interface Expression {
|
||||||
|
@NotNull
|
||||||
|
String renderAsCode();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
Collection<DiType> getTypesToImport();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
DiType getType();
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ class Field {
|
|||||||
|
|
||||||
public static Field create(boolean isPublic, DiType 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;
|
if (init != null) {
|
||||||
|
field.setInitialization(init);
|
||||||
|
}
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ class Field {
|
|||||||
private final String name;
|
private final String name;
|
||||||
private final boolean isPublic;
|
private final boolean isPublic;
|
||||||
|
|
||||||
@Nullable
|
@NotNull
|
||||||
private Expression initialization;
|
private Expression initialization;
|
||||||
|
|
||||||
private final List<SetterDependency> dependencies = Lists.newArrayList();
|
private final List<SetterDependency> dependencies = Lists.newArrayList();
|
||||||
@@ -47,6 +49,7 @@ class Field {
|
|||||||
this.isPublic = isPublic;
|
this.isPublic = isPublic;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.initialization = new InstantiateType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiType getType() {
|
public DiType getType() {
|
||||||
@@ -72,7 +75,7 @@ class Field {
|
|||||||
return prefix + StringUtil.capitalize(getName());
|
return prefix + StringUtil.capitalize(getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NotNull
|
||||||
public Expression getInitialization() {
|
public Expression getInitialization() {
|
||||||
return initialization;
|
return initialization;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,44 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.di;
|
package org.jetbrains.jet.di;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
class GivenExpression implements Expression {
|
class GivenExpression implements Expression {
|
||||||
private final String expression;
|
private final String expression;
|
||||||
|
private final Collection<DiType> typesToImport;
|
||||||
|
|
||||||
GivenExpression(String expression) {
|
|
||||||
|
public GivenExpression(@NotNull String expression) {
|
||||||
|
this(expression, Collections.<DiType>emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public GivenExpression(@NotNull String expression, @NotNull DiType... typesToImport) {
|
||||||
|
this(expression, Arrays.asList(typesToImport));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GivenExpression(@NotNull String expression, @NotNull Class<?>... typesToImport) {
|
||||||
|
this(expression, convertClassesToDiTypes(typesToImport));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<DiType> convertClassesToDiTypes(Class<?>[] typesToImport) {
|
||||||
|
Collection<DiType> types = Lists.newArrayList();
|
||||||
|
for (Class<?> aClass : typesToImport) {
|
||||||
|
types.add(new DiType(aClass));
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GivenExpression(@NotNull String expression, @NotNull Collection<DiType> typesToImport) {
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
|
this.typesToImport = typesToImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExpression() {
|
public String getExpression() {
|
||||||
@@ -32,6 +62,22 @@ class GivenExpression implements Expression {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
return "given<" + expression + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiType getType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String renderAsCode() {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<DiType> getTypesToImport() {
|
||||||
|
return typesToImport;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author abreslav
|
||||||
|
*/
|
||||||
|
public class InstantiateType implements Expression {
|
||||||
|
|
||||||
|
private final DiType type;
|
||||||
|
|
||||||
|
public InstantiateType(@NotNull DiType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InstantiateType(@NotNull Class<?> theClass) {
|
||||||
|
this(new DiType(theClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public DiType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[Instantiate type: " + getType() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String renderAsCode() {
|
||||||
|
throw new UnsupportedOperationException("This should be replaced by some concrete expression by the time this method is called");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<DiType> getTypesToImport() {
|
||||||
|
throw new UnsupportedOperationException("This should be replaced by some concrete expression by the time this method is called");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
package org.jetbrains.jet.di;
|
package org.jetbrains.jet.di;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author abreslav
|
* @author abreslav
|
||||||
*/
|
*/
|
||||||
@@ -32,6 +37,24 @@ public class ParameterExpression implements Expression {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
return "parameter<" + parameter.getName() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String renderAsCode() {
|
||||||
return parameter.getName();
|
return parameter.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<DiType> getTypesToImport() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public DiType getType() {
|
||||||
|
return parameter.getType();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user