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