• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
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.ContributionBinding.FactoryCreationStrategy.DELEGATE;
25 import static dagger.internal.codegen.ContributionBinding.FactoryCreationStrategy.SINGLETON_INSTANCE;
26 import static dagger.internal.codegen.GwtCompatibility.gwtIncompatibleAnnotation;
27 import static dagger.internal.codegen.SourceFiles.bindingTypeElementTypeVariableNames;
28 import static dagger.internal.codegen.SourceFiles.frameworkFieldUsages;
29 import static dagger.internal.codegen.SourceFiles.frameworkTypeUsageStatement;
30 import static dagger.internal.codegen.SourceFiles.generateBindingFieldsForDependencies;
31 import static dagger.internal.codegen.SourceFiles.generatedClassNameForBinding;
32 import static dagger.internal.codegen.SourceFiles.parameterizedGeneratedTypeNameForBinding;
33 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES;
34 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED;
35 import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings;
36 import static dagger.internal.codegen.javapoet.CodeBlocks.makeParametersCodeBlock;
37 import static dagger.internal.codegen.javapoet.TypeNames.factoryOf;
38 import static dagger.model.BindingKind.PROVISION;
39 import static javax.lang.model.element.Modifier.FINAL;
40 import static javax.lang.model.element.Modifier.PRIVATE;
41 import static javax.lang.model.element.Modifier.PUBLIC;
42 import static javax.lang.model.element.Modifier.STATIC;
43 
44 import com.google.common.collect.ImmutableList;
45 import com.google.common.collect.ImmutableMap;
46 import com.google.common.collect.Lists;
47 import com.squareup.javapoet.ClassName;
48 import com.squareup.javapoet.CodeBlock;
49 import com.squareup.javapoet.FieldSpec;
50 import com.squareup.javapoet.MethodSpec;
51 import com.squareup.javapoet.ParameterSpec;
52 import com.squareup.javapoet.TypeName;
53 import com.squareup.javapoet.TypeSpec;
54 import dagger.internal.Factory;
55 import dagger.internal.Preconditions;
56 import dagger.internal.codegen.InjectionMethods.InjectionSiteMethod;
57 import dagger.internal.codegen.InjectionMethods.ProvisionMethod;
58 import dagger.internal.codegen.javapoet.CodeBlocks;
59 import dagger.internal.codegen.langmodel.DaggerElements;
60 import dagger.internal.codegen.langmodel.DaggerTypes;
61 import dagger.model.Key;
62 import java.util.List;
63 import java.util.Optional;
64 import javax.annotation.processing.Filer;
65 import javax.inject.Inject;
66 import javax.lang.model.SourceVersion;
67 import javax.lang.model.element.Element;
68 
69 /**
70  * Generates {@link Factory} implementations from {@link ProvisionBinding} instances for
71  * {@link Inject} constructors.
72  */
73 final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding> {
74   private final DaggerTypes types;
75   private final DaggerElements elements;
76   private final CompilerOptions compilerOptions;
77 
78   @Inject
FactoryGenerator( Filer filer, SourceVersion sourceVersion, DaggerTypes types, DaggerElements elements, CompilerOptions compilerOptions)79   FactoryGenerator(
80       Filer filer,
81       SourceVersion sourceVersion,
82       DaggerTypes types,
83       DaggerElements elements,
84       CompilerOptions compilerOptions) {
85     super(filer, elements, sourceVersion);
86     this.types = types;
87     this.elements = elements;
88     this.compilerOptions = compilerOptions;
89   }
90 
91   @Override
nameGeneratedType(ProvisionBinding binding)92   ClassName nameGeneratedType(ProvisionBinding binding) {
93     return generatedClassNameForBinding(binding);
94   }
95 
96   @Override
originatingElement(ProvisionBinding binding)97   Element originatingElement(ProvisionBinding binding) {
98     // we only create factories for bindings that have a binding element
99     return binding.bindingElement().get();
100   }
101 
102   @Override
write(ClassName generatedTypeName, ProvisionBinding binding)103   Optional<TypeSpec.Builder> write(ClassName generatedTypeName, ProvisionBinding binding) {
104     // We don't want to write out resolved bindings -- we want to write out the generic version.
105     checkArgument(!binding.unresolved().isPresent());
106     checkArgument(binding.bindingElement().isPresent());
107 
108     return binding.factoryCreationStrategy().equals(DELEGATE)
109         ? Optional.empty()
110         : Optional.of(factoryBuilder(binding));
111   }
112 
factoryBuilder(ProvisionBinding binding)113   private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) {
114     TypeSpec.Builder factoryBuilder =
115         classBuilder(nameGeneratedType(binding))
116             .addModifiers(PUBLIC, FINAL)
117             .addSuperinterface(factoryTypeName(binding))
118             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
119 
120     addConstructorAndFields(binding, factoryBuilder);
121     factoryBuilder.addMethod(getMethod(binding));
122     addCreateMethod(binding, factoryBuilder);
123 
124     factoryBuilder.addMethod(
125         ProvisionMethod.create(binding, compilerOptions, elements).toMethodSpec());
126     gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation);
127 
128     return factoryBuilder;
129   }
130 
addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)131   private void addConstructorAndFields(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
132     if (binding.factoryCreationStrategy().equals(SINGLETON_INSTANCE)) {
133       return;
134     }
135     // TODO(user): Make the constructor private?
136     MethodSpec.Builder constructor = constructorBuilder().addModifiers(PUBLIC);
137     constructorParams(binding).forEach(
138         param -> {
139           constructor.addParameter(param).addStatement("this.$1N = $1N", param);
140           factoryBuilder.addField(
141               FieldSpec.builder(param.type, param.name, PRIVATE, FINAL).build());
142         });
143     factoryBuilder.addMethod(constructor.build());
144   }
145 
constructorParams(ProvisionBinding binding)146   private ImmutableList<ParameterSpec> constructorParams(ProvisionBinding binding) {
147     ImmutableList.Builder<ParameterSpec> params = ImmutableList.builder();
148     moduleParameter(binding).ifPresent(params::add);
149     frameworkFields(binding).values().forEach(field -> params.add(toParameter(field)));
150     return params.build();
151   }
152 
moduleParameter(ProvisionBinding binding)153   private Optional<ParameterSpec> moduleParameter(ProvisionBinding binding) {
154     if (binding.requiresModuleInstance()) {
155       // TODO(user, dpb): Should this use contributingModule()?
156       TypeName type = TypeName.get(binding.bindingTypeElement().get().asType());
157       return Optional.of(ParameterSpec.builder(type, "module").build());
158     }
159     return Optional.empty();
160   }
161 
frameworkFields(ProvisionBinding binding)162   private ImmutableMap<Key, FieldSpec> frameworkFields(ProvisionBinding binding) {
163     UniqueNameSet uniqueFieldNames = new UniqueNameSet();
164     // TODO(user, dpb): Add a test for the case when a Factory parameter is named "module".
165     if (binding.requiresModuleInstance()) {
166       uniqueFieldNames.claim("module");
167     }
168     return ImmutableMap.copyOf(
169         transformValues(
170             generateBindingFieldsForDependencies(binding),
171             field ->
172                 FieldSpec.builder(
173                         field.type(), uniqueFieldNames.getUniqueName(field.name()), PRIVATE, FINAL)
174                     .build()));
175   }
176 
addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder)177   private void addCreateMethod(ProvisionBinding binding, TypeSpec.Builder factoryBuilder) {
178     // If constructing a factory for @Inject or @Provides bindings, we use a static create method
179     // so that generated components can avoid having to refer to the generic types
180     // of the factory.  (Otherwise they may have visibility problems referring to the types.)
181     MethodSpec.Builder createMethodBuilder =
182         methodBuilder("create")
183             .addModifiers(PUBLIC, STATIC)
184             .returns(parameterizedGeneratedTypeNameForBinding(binding))
185             .addTypeVariables(bindingTypeElementTypeVariableNames(binding));
186 
187     switch (binding.factoryCreationStrategy()) {
188       case SINGLETON_INSTANCE:
189         FieldSpec.Builder instanceFieldBuilder =
190             FieldSpec.builder(nameGeneratedType(binding), "INSTANCE", PRIVATE, STATIC, FINAL)
191                 .initializer("new $T()", nameGeneratedType(binding));
192 
193         if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) {
194           // If the factory has type parameters, ignore them in the field declaration & initializer
195           instanceFieldBuilder.addAnnotation(suppressWarnings(RAWTYPES));
196 
197           createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED));
198         }
199         createMethodBuilder.addStatement("return INSTANCE");
200         factoryBuilder.addField(instanceFieldBuilder.build());
201         break;
202       case CLASS_CONSTRUCTOR:
203         List<ParameterSpec> params = constructorParams(binding);
204         createMethodBuilder.addParameters(params);
205         createMethodBuilder.addStatement(
206             "return new $T($L)",
207             parameterizedGeneratedTypeNameForBinding(binding),
208             makeParametersCodeBlock(Lists.transform(params, input -> CodeBlock.of("$N", input))));
209         break;
210       default:
211         throw new AssertionError();
212     }
213     factoryBuilder.addMethod(createMethodBuilder.build());
214   }
215 
getMethod(ProvisionBinding binding)216   private MethodSpec getMethod(ProvisionBinding binding) {
217     TypeName providedTypeName = providedTypeName(binding);
218     MethodSpec.Builder getMethod =
219         methodBuilder("get")
220             .addAnnotation(Override.class)
221             .addModifiers(PUBLIC)
222             .returns(providedTypeName);
223 
224     ImmutableMap<Key, FieldSpec> frameworkFields = frameworkFields(binding);
225     CodeBlock parametersCodeBlock =
226         makeParametersCodeBlock(
227             frameworkFieldUsages(binding.provisionDependencies(), frameworkFields).values());
228 
229     if (binding.kind().equals(PROVISION)) {
230       binding
231           .nullableType()
232           .ifPresent(nullableType -> CodeBlocks.addAnnotation(getMethod, nullableType));
233       getMethod.addStatement(
234           "return $L",
235           ProvisionMethod.invoke(
236               binding,
237               request ->
238                   frameworkTypeUsageStatement(
239                       CodeBlock.of("$N", frameworkFields.get(request.key())), request.kind()),
240               nameGeneratedType(binding),
241               binding.requiresModuleInstance()
242                   ? Optional.of(CodeBlock.of("module"))
243                   : Optional.empty(),
244               compilerOptions,
245               elements));
246     } else if (!binding.injectionSites().isEmpty()) {
247       CodeBlock instance = CodeBlock.of("instance");
248       getMethod
249           .addStatement("$1T $2L = new $1T($3L)", providedTypeName, instance, parametersCodeBlock)
250           .addCode(
251               InjectionSiteMethod.invokeAll(
252                   binding.injectionSites(),
253                   nameGeneratedType(binding),
254                   instance,
255                   binding.key().type(),
256                   types,
257                   frameworkFieldUsages(binding.dependencies(), frameworkFields)::get,
258                   elements))
259           .addStatement("return $L", instance);
260     } else {
261       getMethod.addStatement(
262           "return new $T($L)", providedTypeName, parametersCodeBlock);
263     }
264     return getMethod.build();
265   }
266 
providedTypeName(ProvisionBinding binding)267   private static TypeName providedTypeName(ProvisionBinding binding) {
268     return TypeName.get(binding.contributedType());
269   }
270 
factoryTypeName(ProvisionBinding binding)271   private static TypeName factoryTypeName(ProvisionBinding binding) {
272     return factoryOf(providedTypeName(binding));
273   }
274 
toParameter(FieldSpec field)275   private static ParameterSpec toParameter(FieldSpec field) {
276     return ParameterSpec.builder(field.type, field.name).build();
277   }
278 
279   /**
280    * Returns {@code Preconditions.checkNotNull(providesMethodInvocation)} with a message suitable
281    * for {@code @Provides} methods.
282    */
checkNotNullProvidesMethod(CodeBlock providesMethodInvocation)283   static CodeBlock checkNotNullProvidesMethod(CodeBlock providesMethodInvocation) {
284     return CodeBlock.of(
285         "$T.checkNotNull($L, $S)",
286         Preconditions.class,
287         providesMethodInvocation,
288         "Cannot return null from a non-@Nullable @Provides method");
289   }
290 }
291