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;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
class ConstructorCall implements Expression {
|
||||
public class ConstructorCall implements Expression {
|
||||
private final Constructor<?> constructor;
|
||||
private final List<Field> constructorArguments = Lists.newArrayList();
|
||||
|
||||
@@ -39,6 +42,12 @@ class ConstructorCall implements Expression {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return constructor.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String renderAsCode() {
|
||||
StringBuilder builder = new StringBuilder("new " + constructor.getDeclaringClass().getSimpleName() + "(");
|
||||
for (Iterator<Field> iterator = constructorArguments.iterator(); iterator.hasNext(); ) {
|
||||
Field argument = iterator.next();
|
||||
@@ -55,4 +64,15 @@ class ConstructorCall implements Expression {
|
||||
builder.append(")");
|
||||
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 List<Class<?>> implementsList = Lists.newArrayList();
|
||||
private final Collection<Class<?>> imports = Sets.newHashSet();
|
||||
|
||||
public DependencyInjectorGenerator(boolean lazy) {
|
||||
this.lazy = lazy;
|
||||
}
|
||||
@@ -99,7 +102,9 @@ public class DependencyInjectorGenerator {
|
||||
out.println();
|
||||
|
||||
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
|
||||
// out.println();
|
||||
// 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) {
|
||||
addPublicParameter(new DiType(type));
|
||||
}
|
||||
@@ -206,6 +231,17 @@ public class DependencyInjectorGenerator {
|
||||
private void generateImports(PrintStream out, String injectorPackageName) {
|
||||
for (Field field : fields) {
|
||||
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) {
|
||||
generateImportDirectives(out, parameter.getType(), injectorPackageName);
|
||||
@@ -230,7 +266,8 @@ public class DependencyInjectorGenerator {
|
||||
if (type.isPrimitive()) return;
|
||||
String importedPackageName = type.getPackage().getName();
|
||||
if ("java.lang".equals(importedPackageName)
|
||||
|| injectorPackageName.equals(importedPackageName)) {
|
||||
|| injectorPackageName.equals(importedPackageName)
|
||||
|| !imports.add(type)) {
|
||||
return;
|
||||
}
|
||||
out.println("import " + type.getCanonicalName() + ";");
|
||||
@@ -239,10 +276,15 @@ public class DependencyInjectorGenerator {
|
||||
private void generateFields(PrintStream out) {
|
||||
for (Field field : fields) {
|
||||
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) {
|
||||
String indent = " ";
|
||||
|
||||
@@ -277,7 +319,7 @@ public class DependencyInjectorGenerator {
|
||||
for (Field field : fields) {
|
||||
//if (!backsParameter.contains(field) || field.isPublic()) {
|
||||
String prefix = "this.";
|
||||
out.println(indent + prefix + field.getName() + " = " + field.getInitialization() + ";");
|
||||
out.println(indent + prefix + field.getName() + " = " + field.getInitialization().renderAsCode() + ";");
|
||||
//}
|
||||
}
|
||||
out.println();
|
||||
@@ -296,7 +338,7 @@ public class DependencyInjectorGenerator {
|
||||
// call @PostConstruct
|
||||
for (Field field : fields) {
|
||||
// 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) {
|
||||
out.println(indent + field.getName() + "." + postConstruct.getName() + "();");
|
||||
}
|
||||
@@ -335,7 +377,7 @@ public class DependencyInjectorGenerator {
|
||||
out.println(" public void destroy() {");
|
||||
for (Field field : fields) {
|
||||
// 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) {
|
||||
out.println(" " + field.getName() + "." + preDestroy.getName() + "();");
|
||||
}
|
||||
@@ -359,7 +401,7 @@ public class DependencyInjectorGenerator {
|
||||
|
||||
if (lazy && !backsParameter.contains(field)) {
|
||||
Expression initialization = field.getInitialization();
|
||||
assert initialization != null : field;
|
||||
assert initialization instanceof InstantiateType : field;
|
||||
|
||||
// Double-checked locking
|
||||
out.println(indent1 + "if (this." + field.getName() + " == null) {");
|
||||
@@ -400,12 +442,14 @@ public class DependencyInjectorGenerator {
|
||||
if (!satisfied.add(field)) return;
|
||||
if (backsParameter.contains(field)) return;
|
||||
|
||||
if (field.getInitialization() == null) {
|
||||
Expression initialization = field.getInitialization();
|
||||
if (initialization instanceof InstantiateType) {
|
||||
initializeByConstructorCall(field, neededFor);
|
||||
}
|
||||
DiType typeToInitialize = getEffectiveFieldType(field);
|
||||
|
||||
// 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>() {
|
||||
@Override
|
||||
public int compare(Method o1, Method o2) {
|
||||
@@ -455,7 +499,8 @@ public class DependencyInjectorGenerator {
|
||||
}
|
||||
|
||||
private void initializeByConstructorCall(Field field, Field neededFor) {
|
||||
DiType type = field.getType();
|
||||
Expression initialization = field.getInitialization();
|
||||
DiType type = ((InstantiateType) initialization).getType();
|
||||
|
||||
if (type.getClazz().isInterface()) {
|
||||
throw new IllegalArgumentException("cannot instantiate interface: " + type.getClazz().getName() + " needed for " + neededFor);
|
||||
|
||||
@@ -16,7 +16,21 @@
|
||||
|
||||
package org.jetbrains.jet.di;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
Field field = new Field(isPublic, type, name);
|
||||
field.initialization = init;
|
||||
if (init != null) {
|
||||
field.setInitialization(init);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
@@ -38,7 +40,7 @@ class Field {
|
||||
private final String name;
|
||||
private final boolean isPublic;
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
private Expression initialization;
|
||||
|
||||
private final List<SetterDependency> dependencies = Lists.newArrayList();
|
||||
@@ -47,6 +49,7 @@ class Field {
|
||||
this.isPublic = isPublic;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.initialization = new InstantiateType(type);
|
||||
}
|
||||
|
||||
public DiType getType() {
|
||||
@@ -72,7 +75,7 @@ class Field {
|
||||
return prefix + StringUtil.capitalize(getName());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
public Expression getInitialization() {
|
||||
return initialization;
|
||||
}
|
||||
|
||||
@@ -16,14 +16,44 @@
|
||||
|
||||
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
|
||||
*/
|
||||
class GivenExpression implements 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.typesToImport = typesToImport;
|
||||
}
|
||||
|
||||
public String getExpression() {
|
||||
@@ -32,6 +62,22 @@ class GivenExpression implements Expression {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "given<" + expression + ">";
|
||||
}
|
||||
|
||||
public DiType getType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String renderAsCode() {
|
||||
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;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
@@ -32,6 +37,24 @@ public class ParameterExpression implements Expression {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "parameter<" + parameter.getName() + ">";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String renderAsCode() {
|
||||
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