diff --git a/injector-generator/src/org/jetbrains/jet/di/DependencyInjectorGenerator.java b/injector-generator/src/org/jetbrains/jet/di/DependencyInjectorGenerator.java index a0af388cecf..aab9e2ab5ba 100644 --- a/injector-generator/src/org/jetbrains/jet/di/DependencyInjectorGenerator.java +++ b/injector-generator/src/org/jetbrains/jet/di/DependencyInjectorGenerator.java @@ -45,6 +45,7 @@ public class DependencyInjectorGenerator { private final boolean lazy; private final Set fields = Sets.newLinkedHashSet(); private final Set parameters = Sets.newLinkedHashSet(); + private final Set factoryMethods = Sets.newLinkedHashSet(); private final Dependencies dependencies = new Dependencies(); @@ -109,6 +110,10 @@ public class DependencyInjectorGenerator { generateDestroy(injectorClassName, out); out.println(); generateGetters(out); + if (!factoryMethods.isEmpty()) { + out.println(); + generateFactoryMethods(out); + } // Needed to fix double-checked locking // out.println(); // generateMakeFunction(out); @@ -221,6 +226,26 @@ public class DependencyInjectorGenerator { return field; } + public void addFactoryMethod(@NotNull Class returnType, Class... parameterTypes) { + List types = Lists.newArrayList(); + for (Class type : parameterTypes) { + types.add(new DiType(type)); + } + addFactoryMethod(new DiType(returnType), types); + } + + public void addFactoryMethod(@NotNull DiType returnType, DiType... parameterTypes) { + addFactoryMethod(returnType, Arrays.asList(parameterTypes)); + } + + public void addFactoryMethod(@NotNull DiType returnType, @NotNull Collection parameterTypes) { + List parameters = Lists.newArrayList(); + for (DiType type : parameterTypes) { + parameters.add(new Parameter(type, var(type), null, false)); + } + factoryMethods.add(new FactoryMethod("create" + returnType.getSimpleName(), returnType, parameters)); + } + private void generateImports(PrintStream out, String injectorPackageName) { for (Field field : fields) { generateImportDirectives(out, field.getType(), injectorPackageName); @@ -313,48 +338,13 @@ public class DependencyInjectorGenerator { } } else { - generateInitializingCode(p, fields); + InjectionLogicGenerator.FIELDS.generate(p, fields); } p.popIndent(); p.println("}"); } - private static void generateInitializingCode(@NotNull Printer p, @NotNull Collection fields) { - // Initialize fields - for (Field field : fields) { - //if (!backsParameter.contains(field) || field.isPublic()) { - p.println("this.", field.getName(), " = ", field.getInitialization().renderAsCode(), ";"); - //} - } - p.printlnWithNoIndent(); - - // Call setters - for (Field field : fields) { - for (SetterDependency dependency : field.getDependencies()) { - String prefix = field.isPublic() ? "this." : ""; - String dependencyName = dependency.getDependency().getName(); - String dependentName = dependency.getDependent().getName(); - p.println(prefix, dependentName, ".", dependency.getSetterName(), "(", dependencyName, ");"); - } - if (!field.getDependencies().isEmpty()) { - p.printlnWithNoIndent(); - } - } - - // call @PostConstruct - for (Field field : fields) { - // TODO: type of field may be different from type of object - List postConstructMethods = getPostConstructMethods(getEffectiveFieldType(field).getClazz()); - for (Method postConstruct : postConstructMethods) { - p.println(field.getName(), ".", postConstruct.getName(), "();"); - } - if (postConstructMethods.size() > 0) { - p.printlnWithNoIndent(); - } - } - } - private static List getPostConstructMethods(Class clazz) { return getInjectSpecialMethods(clazz, PostConstruct.class); } @@ -442,6 +432,60 @@ public class DependencyInjectorGenerator { } } + private void generateFactoryMethods(PrintStream out) { + Printer p = new Printer(out); + p.pushIndent(); + for (FactoryMethod method : factoryMethods) { + generateFactoryMethod(p, method); + } + } + + private void generateFactoryMethod(Printer p, FactoryMethod method) { + Dependencies localDependencies = new Dependencies(); + + Map parameterToField = Maps.newHashMap(); + for (Parameter parameter : method.getParameters()) { + Field field = new Field(true, parameter.getType(), parameter.getName()); + localDependencies.addSatisfiedField(field); + parameterToField.put(parameter, field); + } + + for (Field storedField : fields) { + localDependencies.addSatisfiedField(storedField); + } + + Field resultField = new Field(true, method.getReturnType(), "_result"); + localDependencies.addField(resultField); + + Collection fields = Lists.newArrayList(localDependencies.satisfyDependencies()); + fields.add(resultField); + + p.println("public ", type(method.getReturnType()), " ", method.getName(), "("); + p.pushIndent(); + for (Iterator iterator = method.getParameters().iterator(); iterator.hasNext(); ) { + Parameter parameter = iterator.next(); + p.print(type(parameter.getType()), " ", parameter.getName()); + if (iterator.hasNext()) { + p.printlnWithNoIndent(", "); + } + } + p.println(); + p.popIndent(); + p.println(") {"); + p.pushIndent(); + + InjectionLogicGenerator.LOCAL_VARIABLES.generate(p, fields); + + p.println("return ", resultField.getName(), ";"); + + p.popIndent(); + p.println("}"); + } + + private String type(DiType type) { + return type.getSimpleName(); + } + private static class Dependencies { private final Set allFields = Sets.newLinkedHashSet(); @@ -571,6 +615,84 @@ public class DependencyInjectorGenerator { } } + private static abstract class InjectionLogicGenerator { + + public static final InjectionLogicGenerator FIELDS = new InjectionLogicGenerator() { + @Override + public String prefixForPostConstructorCall(Field field) { + return ""; + } + + @Override + public String prefixForSetterCall(Field field) { + return field.isPublic() ? "this." : ""; + } + + @Override + public String prefixForInitialization(Field field) { + return "this."; + } + }; + + public static final InjectionLogicGenerator LOCAL_VARIABLES = new InjectionLogicGenerator() { + @Override + public String prefixForPostConstructorCall(Field field) { + return ""; + } + + @Override + public String prefixForSetterCall(Field field) { + return ""; + } + + @Override + public String prefixForInitialization(Field field) { + return field.getType() + " "; + } + }; + + public final void generate(@NotNull Printer p, @NotNull Collection fields) { + // Initialize fields + for (Field field : fields) { + //if (!backsParameter.contains(field) || field.isPublic()) { + p.println(prefixForInitialization(field), field.getName(), " = ", field.getInitialization().renderAsCode(), ";"); + //} + } + p.printlnWithNoIndent(); + + // Call setters + for (Field field : fields) { + for (SetterDependency dependency : field.getDependencies()) { + String prefix = prefixForSetterCall(field); + String dependencyName = dependency.getDependency().getName(); + String dependentName = dependency.getDependent().getName(); + p.println(prefix, dependentName, ".", dependency.getSetterName(), "(", dependencyName, ");"); + } + if (!field.getDependencies().isEmpty()) { + p.printlnWithNoIndent(); + } + } + + // call @PostConstruct + for (Field field : fields) { + // TODO: type of field may be different from type of object + List postConstructMethods = getPostConstructMethods(getEffectiveFieldType(field).getClazz()); + for (Method postConstruct : postConstructMethods) { + p.println(prefixForPostConstructorCall(field), field.getName(), ".", postConstruct.getName(), "();"); + } + if (postConstructMethods.size() > 0) { + p.printlnWithNoIndent(); + } + } + } + + protected abstract String prefixForInitialization(Field field); + + protected abstract String prefixForSetterCall(Field field); + + protected abstract String prefixForPostConstructorCall(Field field); + } + private static String var(@NotNull DiType type) { StringBuilder sb = new StringBuilder(); sb.append(StringUtil.decapitalize(type.getClazz().getSimpleName().replaceFirst("(?<=.)Impl$", ""))); diff --git a/injector-generator/src/org/jetbrains/jet/di/FactoryMethod.java b/injector-generator/src/org/jetbrains/jet/di/FactoryMethod.java new file mode 100644 index 00000000000..fda421ce6eb --- /dev/null +++ b/injector-generator/src/org/jetbrains/jet/di/FactoryMethod.java @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2013 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.List; + +public class FactoryMethod { + private final String name; + private final List parameters; + private final DiType returnType; + + public FactoryMethod(@NotNull String name, @NotNull DiType returnType, @NotNull List parameters) { + this.name = name; + this.parameters = parameters; + this.returnType = returnType; + } + + @NotNull + public String getName() { + return name; + } + + @NotNull + public List getParameters() { + return parameters; + } + + @NotNull + public DiType getReturnType() { + return returnType; + } +}