| /* |
| * Copyright (C) 2013 Square, Inc. |
| * Copyright (C) 2013 Google, Inc. |
| * |
| * 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 com.google.autofactory; |
| |
| import static com.google.autofactory.CodeGen.strippedTypeName; |
| import static java.lang.reflect.Modifier.FINAL; |
| import static java.lang.reflect.Modifier.PUBLIC; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import javax.annotation.processing.ProcessingEnvironment; |
| import javax.inject.Inject; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.TypeMirror; |
| |
| import com.google.autofactory.ProcessorUtils.InjectedClass; |
| import com.squareup.java.JavaWriter; |
| |
| /** |
| * A class that generates a Factory implementation for a given class. |
| */ |
| public final class FactoryAdapterGenerator extends AbstractGenerator { |
| |
| private static final int PACKAGE = 0; |
| static final String FACTORY_IMPLEMENTATION = "$AutoFactory"; |
| |
| FactoryAdapterGenerator(ProcessingEnvironment env) { |
| super(env); |
| } |
| |
| /** |
| * Write a companion class for a factory that prodouces {@code type} and |
| * extends {@link com.google.autofactory.internal.Binding}. |
| * |
| * @param constructor the injectable constructor, or null if this binding |
| * supports members injection only. |
| */ |
| void generate(InjectedClass clazz, TypeElement factory, ExecutableElement method) |
| throws IOException { |
| TypeElement type = clazz.type; |
| String packageName = CodeGen.getPackage(type).getQualifiedName().toString(); |
| String strippedTypeName = strippedTypeName(type.getQualifiedName().toString(), packageName); |
| String adapterName = CodeGen.adapterName(factory, FACTORY_IMPLEMENTATION); |
| JavaWriter writer = newWriter(adapterName, type); |
| |
| List<? extends VariableElement> parameters = (clazz.constructor != null) |
| ? clazz.constructor.getParameters() |
| : new ArrayList<VariableElement>(); |
| |
| writer.emitEndOfLineComment("Generated by com.google.autofactory's generator. Do not edit."); |
| writer.emitPackage(packageName); |
| writer.emitEmptyLine(); |
| writer.emitImports(getImports(factory, clazz.fields, parameters)); |
| |
| writer.emitEmptyLine(); |
| writer.emitJavadoc("An implementation of {@code %s} to act as a factory for {@code %s}", |
| factory.getSimpleName(), strippedTypeName); |
| writer.beginType(adapterName, "class", PUBLIC | FINAL, null, |
| factory.getSimpleName().toString()); |
| |
| for (VariableElement parameter : parameters) { |
| if (parameter.getAnnotation(Param.class) == null) { |
| writer.emitAnnotation(Inject.class); |
| writer.emitField(CodeGen.typeToString(parameter.asType()), |
| parameterName(false, parameter), PACKAGE); |
| } |
| } |
| for (Element field : clazz.fields) { |
| if (field.getAnnotation(Inject.class) != null) { |
| writer.emitAnnotation(Inject.class); |
| writer.emitField(CodeGen.typeToString(field.asType()), |
| fieldName(false, field), PACKAGE); |
| } |
| } |
| |
| if (!hasDependencies(clazz)) { // If no deps, generate an @Inject constructor. |
| writer.emitEmptyLine(); |
| writer.emitAnnotation(Inject.class); |
| writer.beginMethod(null, adapterName, PUBLIC); |
| writer.endMethod(); |
| } |
| |
| writer.emitEmptyLine(); |
| writer.emitAnnotation(Override.class); |
| writer.beginMethod(strippedTypeName, method.getSimpleName().toString(), PUBLIC, |
| getParameterPairs(method)); |
| StringBuilder newInstance = new StringBuilder(); |
| newInstance.append(strippedTypeName).append(" instance = new "); |
| newInstance.append(strippedTypeName).append('('); |
| boolean first = true; |
| for (VariableElement parameter : parameters) { |
| if (!first) newInstance.append(", "); |
| else first = false; |
| newInstance.append(parameterName(false, parameter)); |
| } |
| newInstance.append(')'); |
| writer.emitStatement(newInstance.toString()); |
| for (Element field : clazz.fields) { |
| writer.emitStatement("instance.%s = %s", field.getSimpleName(), |
| fieldName(false, field)); |
| } |
| writer.emitStatement("return instance"); |
| writer.endMethod(); |
| |
| writer.endType(); |
| writer.close(); |
| } |
| |
| private boolean hasDependencies(InjectedClass clazz) { |
| for (Element e : clazz.fields) { |
| if (e.getAnnotation(Inject.class) != null) return true; |
| } |
| if (clazz.constructor != null && clazz.constructor.getAnnotation(Inject.class) != null) { |
| for (Element e : clazz.constructor.getParameters()) { |
| if (e.getAnnotation(Param.class) == null) return true; |
| } |
| } |
| return false; |
| } |
| |
| private String[] getParameterPairs(ExecutableElement method) { |
| List<String> paramPairs = new ArrayList<String>(); |
| for (VariableElement element : method.getParameters()) { |
| paramPairs.add(element.asType().toString()); |
| paramPairs.add(element.getSimpleName().toString()); |
| } |
| return paramPairs.toArray(new String[paramPairs.size()]); |
| } |
| |
| private Set<String> getImports(TypeElement factory, List<Element> fields, |
| List<? extends VariableElement> parameters) { |
| Set<String> imports = new LinkedHashSet<String>(); |
| imports.add(factory.getQualifiedName().toString()); |
| imports.add(Inject.class.getName()); |
| collectImportsFromElements(fields, imports); |
| collectImportsFromElements(parameters, imports); |
| return imports; |
| } |
| |
| private void collectImportsFromElements(List<? extends Element> elements, Set<String> imports) { |
| for (Element e : elements) { |
| TypeMirror tm = e.asType(); |
| if (!tm.getKind().isPrimitive() && !tm.toString().startsWith("java.lang")) { |
| imports.add(tm.toString()); |
| } |
| } |
| } |
| } |