1 /*
2  * Copyright (C) 2014 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.writing;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.collect.Maps.transformValues;
21 import static com.squareup.javapoet.MethodSpec.constructorBuilder;
22 import static com.squareup.javapoet.MethodSpec.methodBuilder;
23 import static com.squareup.javapoet.TypeSpec.classBuilder;
24 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedParameters;
25 import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.DELEGATE;
26 import static dagger.internal.codegen.binding.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
27 import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames;
28 import static dagger.internal.codegen.binding.SourceFiles.frameworkFieldUsages;
29 import static dagger.internal.codegen.binding.SourceFiles.frameworkTypeUsageStatement;
30 import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies;
31 import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding;
32 import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding;
33 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
34 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
35 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
36 import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
37 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
38 import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
39 import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation;
40 import static dagger.model.BindingKind.PROVISION;
41 import static javax.lang.model.element.Modifier.FINAL;
42 import static javax.lang.model.element.Modifier.PRIVATE;
43 import static javax.lang.model.element.Modifier.PUBLIC;
44 import static javax.lang.model.element.Modifier.STATIC;
45 
46 import com.google.common.collect.ImmutableList;
47 import com.google.common.collect.ImmutableMap;
48 import com.google.common.collect.Lists;
49 import com.squareup.javapoet.ClassName;
50 import com.squareup.javapoet.CodeBlock;
51 import com.squareup.javapoet.FieldSpec;
52 import com.squareup.javapoet.MethodSpec;
53 import com.squareup.javapoet.ParameterSpec;
54 import com.squareup.javapoet.TypeName;
55 import com.squareup.javapoet.TypeSpec;
56 import dagger.internal.Factory;
57 import dagger.internal.codegen.base.SourceFileGenerator;
58 import dagger.internal.codegen.base.UniqueNameSet;
59 import dagger.internal.codegen.binding.ProvisionBinding;
60 import dagger.internal.codegen.compileroption.CompilerOptions;
61 import dagger.internal.codegen.javapoet.CodeBlocks;
62 import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
63 import dagger.internal.codegen.langmodel.DaggerElements;
64 import dagger.internal.codegen.langmodel.DaggerTypes;
65 import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod;
66 import dagger.internal.codegen.writing.InjectionMethods.ProvisionMethod;
67 import dagger.model.BindingKind;
68 import dagger.model.DependencyRequest;
69 import java.util.List;
70 import java.util.Optional;
71 import javax.annotation.processing.Filer;
72 import javax.inject.Inject;
73 import javax.lang.model.SourceVersion;
74 import javax.lang.model.element.Element;
75 
76 /**
77  * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for {@link
78  * Inject} constructors.
79  */
80 public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
81   private final DaggerTypes types;
82   private final CompilerOptions compilerOptions;
83   private final KotlinMetadataUtil metadataUtil;
84 
85   @Inject
FactoryGenerator( Filer filer, SourceVersion sourceVersion, DaggerTypes types, DaggerElements elements, CompilerOptions compilerOptions, KotlinMetadataUtil metadataUtil)86   FactoryGenerator(
87       Filer filer,
88       SourceVersion sourceVersion,
89       DaggerTypes types,
90       DaggerElements elements,
91       CompilerOptions compilerOptions,
92       KotlinMetadataUtil metadataUtil) {
93     super(filer, elements, sourceVersion);
94     this.types = types;
95     this.compilerOptions = compilerOptions;
96     this.metadataUtil = metadataUtil;
97   }
98 
99   @Override
nameGeneratedType(ProvisionBinding binding)100   public ClassName nameGeneratedType(ProvisionBinding binding) {
101     return generatedClassNameForBinding(binding);
102   }
103 
104   @Override
originatingElement(ProvisionBinding binding)105   public Element originatingElement(ProvisionBinding binding) {
106     // we only create factories for bindings that have a binding element
107     return binding.bindingElement().get();
108   }
109 
110   @Override
write(ProvisionBinding binding)111   public Optional<TypeSpec.Builder> write(ProvisionBinding binding) {
112     // We don't want to write out resolved bindings -- we want to write out the generic version.
113     checkArgument(!binding.unresolved().isPresent());
114     checkArgument(binding.bindingElement().isPresent());
115 
116     if (binding.factoryCreationStrategy().equals(DELEGATE)) {
117       return Optional.empty();
118     }
119 
120     return Optional.of(factoryBuilder(binding));
121   }
122 
factoryBuilder(ProvisionBinding binding)123   private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
124     TypeSpec.Builder factoryBuilder =
125         classBuilder(nameGeneratedType(binding))
126             .addModifiers(PUBLIC, FINAL)
127             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
128 
129     factoryTypeName(binding).ifPresent(factoryBuilder::addSuperinterface);
130     addConstructorAndFields(binding, factoryBuilder);
131     factoryBuilder.addMethod(getMethod(binding));
132     addCreateMethod(binding, factoryBuilder);
133 
134     factoryBuilder.addMethod(ProvisionMethod.create(binding, compilerOptions, metadataUtil));
135     gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
136 
137     return factoryBuilder;
138   }
139 
addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)140   private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
141     if (binding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)) {
142       return;
143     }
144     // TODO(bcorso): Make the constructor private?
145     MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
146     constructorParams(binding).forEach(
147         param -> {
148           constructor.addParameter(param).addStatement("this.$1N = $1N", param);
149           factoryBuilder.addField(
150               FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build());
151         });
152     factoryBuilder.addMethod(constructor.build());
153   }
154 
constructorParams(ProvisionBinding binding)155   private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) {
156     ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder();
157     moduleParameter(binding).ifPresent(params::add);
158     frameworkFields(binding).values().forEach(field -> params.add(toParameter(field)));
159     return params.build();
160   }
161 
moduleParameter(ProvisionBinding binding)162   private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
163     if (binding.requiresModuleInstance()) {
164       // TODO(bcorso, dpb): Should this use contributingModule()?
165       TypeName type = TypeName.get(binding.bindingTypeElement().get().asType());
166       return Optional.of(ParameterSpec.builder(type, "module").build());
167     }
168     return Optional.empty();
169   }
170 
frameworkFields(ProvisionBinding binding)171   private ImmutableMap<DependencyRequest, FieldSpec> frameworkFields(ProvisionBinding binding) {
172     UniqueNameSet uniqueFieldNames = new UniqueNameSet();
173     // TODO(bcorso, dpb): Add a test for the case when a Factory parameter is named "module".
174     moduleParameter(binding).ifPresent(module -> uniqueFieldNames.claim(module.name));
175     return ImmutableMap.copyOf(
176         transformValues(
177             generateBindingFieldsForDependencies(binding),
178             field ->
179                 FieldSpec.builder(
180                         field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
181                     .build()));
182   }
183 
addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)184   private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
185     // If constructing a factory for @Inject or @Provides bindings, we use a static create method
186     // so that generated components can avoid having to refer to the generic types
187     // of the factory.  (Otherwise they may have visibility problems referring to the types.)
188     MethodSpec.Builder createMethodBuilder =
189         methodBuilder("create")
190             .addModifiers(PUBLIC, STATIC)
191             .returns(parameterizedGeneratedTypeNameForBinding(binding))
192             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
193 
194     switch (binding.factoryCreationStrategy()) {
195       case SINGLETON_INSTANCE:
196         FieldSpec.Builder instanceFieldBuilder =
197             FieldSpec.builder(nameGeneratedType(binding), "INSTANCE", PRIVATE, STATIC, FINAL)
198                 .initializer("new $T()", nameGeneratedType(binding));
199 
200         if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
201           // If the factory has type parameters, ignore them in the field declaration & initializer
202           instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
203           createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
204         }
205 
206         ClassName instanceHolderName = nameGeneratedType(binding).nestedClass("InstanceHolder");
207         createMethodBuilder.addStatement("return $T.INSTANCE", instanceHolderName);
208         factoryBuilder.addType(
209             TypeSpec.classBuilder(instanceHolderName)
210                 .addModifiers(PRIVATE, STATIC, FINAL)
211                 .addField(instanceFieldBuilder.build())
212                 .build());
213         break;
214       case CLASS_CONSTRUCTOR:
215         List<ParameterSpec> params = constructorParams(binding);
216         createMethodBuilder.addParameters(params);
217         createMethodBuilder.addStatement(
218             "return new $T($L)",
219             parameterizedGeneratedTypeNameForBinding(binding),
220             makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input))));
221         break;
222       default:
223         throw new AssertionError();
224     }
225     factoryBuilder.addMethod(createMethodBuilder.build());
226   }
227 
getMethod(ProvisionBinding binding)228   private MethodSpec getMethod(ProvisionBinding binding) {
229     TypeName providedTypeName = providedTypeName(binding);
230     MethodSpec.Builder getMethod =
231         methodBuilder("get")
232             .addModifiers(PUBLIC)
233             .returns(providedTypeName)
234             .addParameters(
235                 // The 'get' method for an assisted injection type takes in the assisted parameters.
236                 assistedParameters(binding).stream()
237                     .map(ParameterSpec::get)
238                     .collect(toImmutableList()));
239 
240     if (factoryTypeName(binding).isPresent()) {
241       getMethod.addAnnotation(Override.class);
242     }
243 
244     ImmutableMap<DependencyRequest, FieldSpec> frameworkFields = frameworkFields(binding);
245     CodeBlock invokeNewInstance =
246         ProvisionMethod.invoke(
247             binding,
248             request ->
249                 frameworkTypeUsageStatement(
250                     CodeBlock.of("$N", frameworkFields.get(request)), request.kind()),
251             nameGeneratedType(binding),
252             moduleParameter(binding).map(module -> CodeBlock.of("$N", module)),
253             compilerOptions,
254             metadataUtil);
255 
256     if (binding.kind().equals(PROVISION)) {
257       binding
258           .nullableType()
259           .ifPresent(nullableType -> CodeBlocks.addAnnotation(getMethod, nullableType));
260       getMethod.addStatement("return $L", invokeNewInstance);
261     } else if (!binding.injectionSites().isEmpty()) {
262       CodeBlock instance = CodeBlock.of("instance");
263       getMethod
264           .addStatement("$T $L = $L", providedTypeName, instance, invokeNewInstance)
265           .addCode(
266               InjectionSiteMethod.invokeAll(
267                   binding.injectionSites(),
268                   nameGeneratedType(binding),
269                   instance,
270                   binding.key().type(),
271                   frameworkFieldUsages(binding.dependencies(), frameworkFields)::get,
272                   types,
273                   metadataUtil))
274           .addStatement("return $L", instance);
275     } else {
276       getMethod.addStatement("return $L", invokeNewInstance);
277     }
278     return getMethod.build();
279   }
280 
providedTypeName(ProvisionBinding binding)281   private static TypeName providedTypeName(ProvisionBinding binding) {
282     return TypeName.get(binding.contributedType());
283   }
284 
factoryTypeName(ProvisionBinding binding)285   private static Optional<TypeName> factoryTypeName(ProvisionBinding binding) {
286     return binding.kind() == BindingKind.ASSISTED_INJECTION
287         ? Optional.empty()
288         : Optional.of(factoryOf(providedTypeName(binding)));
289   }
290 
toParameter(FieldSpec field)291   private static ParameterSpec toParameter(FieldSpec field) {
292     return ParameterSpec.builder(field.type, field.name).build();
293   }
294 }
295