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.checkState; 20 import static com.squareup.javapoet.MethodSpec.constructorBuilder; 21 import static com.squareup.javapoet.MethodSpec.methodBuilder; 22 import static com.squareup.javapoet.TypeSpec.classBuilder; 23 import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors; 24 import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConstructors; 25 import static dagger.internal.codegen.binding.SourceFiles.bindingTypeElementTypeVariableNames; 26 import static dagger.internal.codegen.binding.SourceFiles.frameworkFieldUsages; 27 import static dagger.internal.codegen.binding.SourceFiles.generateBindingFieldsForDependencies; 28 import static dagger.internal.codegen.binding.SourceFiles.membersInjectorNameForType; 29 import static dagger.internal.codegen.binding.SourceFiles.parameterizedGeneratedTypeNameForBinding; 30 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES; 31 import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; 32 import static dagger.internal.codegen.javapoet.AnnotationSpecs.suppressWarnings; 33 import static dagger.internal.codegen.javapoet.CodeBlocks.toParametersCodeBlock; 34 import static dagger.internal.codegen.javapoet.TypeNames.membersInjectorOf; 35 import static dagger.internal.codegen.langmodel.Accessibility.isTypeAccessibleFrom; 36 import static dagger.internal.codegen.writing.GwtCompatibility.gwtIncompatibleAnnotation; 37 import static javax.lang.model.element.Modifier.FINAL; 38 import static javax.lang.model.element.Modifier.PRIVATE; 39 import static javax.lang.model.element.Modifier.PUBLIC; 40 import static javax.lang.model.element.Modifier.STATIC; 41 42 import com.google.common.collect.ImmutableList; 43 import com.google.common.collect.ImmutableMap; 44 import com.squareup.javapoet.ClassName; 45 import com.squareup.javapoet.CodeBlock; 46 import com.squareup.javapoet.FieldSpec; 47 import com.squareup.javapoet.MethodSpec; 48 import com.squareup.javapoet.ParameterSpec; 49 import com.squareup.javapoet.TypeName; 50 import com.squareup.javapoet.TypeSpec; 51 import com.squareup.javapoet.TypeVariableName; 52 import dagger.MembersInjector; 53 import dagger.internal.codegen.base.SourceFileGenerator; 54 import dagger.internal.codegen.base.UniqueNameSet; 55 import dagger.internal.codegen.binding.FrameworkField; 56 import dagger.internal.codegen.binding.MembersInjectionBinding; 57 import dagger.internal.codegen.binding.MembersInjectionBinding.InjectionSite; 58 import dagger.internal.codegen.kotlin.KotlinMetadataUtil; 59 import dagger.internal.codegen.langmodel.DaggerElements; 60 import dagger.internal.codegen.langmodel.DaggerTypes; 61 import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod; 62 import dagger.model.DependencyRequest; 63 import java.util.Map.Entry; 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 MembersInjector} implementations from {@link MembersInjectionBinding} instances. 71 */ 72 public final class MembersInjectorGenerator extends SourceFileGenerator<MembersInjectionBinding> { 73 private final DaggerTypes types; 74 private final KotlinMetadataUtil metadataUtil; 75 76 @Inject MembersInjectorGenerator( Filer filer, DaggerElements elements, DaggerTypes types, SourceVersion sourceVersion, KotlinMetadataUtil metadataUtil)77 MembersInjectorGenerator( 78 Filer filer, 79 DaggerElements elements, 80 DaggerTypes types, 81 SourceVersion sourceVersion, 82 KotlinMetadataUtil metadataUtil) { 83 super(filer, elements, sourceVersion); 84 this.types = types; 85 this.metadataUtil = metadataUtil; 86 } 87 88 @Override originatingElement(MembersInjectionBinding binding)89 public Element originatingElement(MembersInjectionBinding binding) { 90 return binding.membersInjectedType(); 91 } 92 93 @Override topLevelTypes(MembersInjectionBinding binding)94 public ImmutableList<TypeSpec.Builder> topLevelTypes(MembersInjectionBinding binding) { 95 // Empty members injection bindings are special and don't need source files. 96 if (binding.injectionSites().isEmpty()) { 97 return ImmutableList.of(); 98 } 99 100 // Members injectors for classes with no local injection sites and no @Inject 101 // constructor are unused. 102 if (!binding.hasLocalInjectionSites() 103 && injectedConstructors(binding.membersInjectedType()).isEmpty() 104 && assistedInjectedConstructors(binding.membersInjectedType()).isEmpty()) { 105 return ImmutableList.of(); 106 } 107 108 109 // We don't want to write out resolved bindings -- we want to write out the generic version. 110 checkState( 111 !binding.unresolved().isPresent(), 112 "tried to generate a MembersInjector for a binding of a resolved generic type: %s", 113 binding); 114 115 ClassName generatedTypeName = membersInjectorNameForType(binding.membersInjectedType()); 116 ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding); 117 TypeSpec.Builder injectorTypeBuilder = 118 classBuilder(generatedTypeName) 119 .addModifiers(PUBLIC, FINAL) 120 .addTypeVariables(typeParameters); 121 122 TypeName injectedTypeName = TypeName.get(binding.key().type()); 123 TypeName implementedType = membersInjectorOf(injectedTypeName); 124 injectorTypeBuilder.addSuperinterface(implementedType); 125 126 MethodSpec.Builder injectMembersBuilder = 127 methodBuilder("injectMembers") 128 .addModifiers(PUBLIC) 129 .addAnnotation(Override.class) 130 .addParameter(injectedTypeName, "instance"); 131 132 ImmutableMap<DependencyRequest, FrameworkField> fields = 133 generateBindingFieldsForDependencies(binding); 134 135 ImmutableMap.Builder<DependencyRequest, FieldSpec> dependencyFieldsBuilder = 136 ImmutableMap.builder(); 137 138 MethodSpec.Builder constructorBuilder = constructorBuilder().addModifiers(PUBLIC); 139 140 // We use a static create method so that generated components can avoid having 141 // to refer to the generic types of the factory. 142 // (Otherwise they may have visibility problems referring to the types.) 143 MethodSpec.Builder createMethodBuilder = 144 methodBuilder("create") 145 .returns(implementedType) 146 .addModifiers(PUBLIC, STATIC) 147 .addTypeVariables(typeParameters); 148 149 createMethodBuilder.addCode( 150 "return new $T(", parameterizedGeneratedTypeNameForBinding(binding)); 151 ImmutableList.Builder<CodeBlock> constructorInvocationParameters = ImmutableList.builder(); 152 153 boolean usesRawFrameworkTypes = false; 154 UniqueNameSet fieldNames = new UniqueNameSet(); 155 for (Entry<DependencyRequest, FrameworkField> fieldEntry : fields.entrySet()) { 156 DependencyRequest dependency = fieldEntry.getKey(); 157 FrameworkField bindingField = fieldEntry.getValue(); 158 159 // If the dependency type is not visible to this members injector, then use the raw framework 160 // type for the field. 161 boolean useRawFrameworkType = 162 !isTypeAccessibleFrom(dependency.key().type(), generatedTypeName.packageName()); 163 164 String fieldName = fieldNames.getUniqueName(bindingField.name()); 165 TypeName fieldType = useRawFrameworkType ? bindingField.type().rawType : bindingField.type(); 166 FieldSpec.Builder fieldBuilder = FieldSpec.builder(fieldType, fieldName, PRIVATE, FINAL); 167 ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(fieldType, fieldName); 168 169 // If we're using the raw type for the field, then suppress the injectMembers method's 170 // unchecked-type warning and the field's and the constructor and create-method's 171 // parameters' raw-type warnings. 172 if (useRawFrameworkType) { 173 usesRawFrameworkTypes = true; 174 fieldBuilder.addAnnotation(suppressWarnings(RAWTYPES)); 175 parameterBuilder.addAnnotation(suppressWarnings(RAWTYPES)); 176 } 177 constructorBuilder.addParameter(parameterBuilder.build()); 178 createMethodBuilder.addParameter(parameterBuilder.build()); 179 180 FieldSpec field = fieldBuilder.build(); 181 injectorTypeBuilder.addField(field); 182 constructorBuilder.addStatement("this.$1N = $1N", field); 183 dependencyFieldsBuilder.put(dependency, field); 184 constructorInvocationParameters.add(CodeBlock.of("$N", field)); 185 } 186 187 createMethodBuilder.addCode( 188 constructorInvocationParameters.build().stream().collect(toParametersCodeBlock())); 189 createMethodBuilder.addCode(");"); 190 191 injectorTypeBuilder.addMethod(constructorBuilder.build()); 192 injectorTypeBuilder.addMethod(createMethodBuilder.build()); 193 194 ImmutableMap<DependencyRequest, FieldSpec> dependencyFields = dependencyFieldsBuilder.build(); 195 196 injectMembersBuilder.addCode( 197 InjectionSiteMethod.invokeAll( 198 binding.injectionSites(), 199 generatedTypeName, 200 CodeBlock.of("instance"), 201 binding.key().type(), 202 frameworkFieldUsages(binding.dependencies(), dependencyFields)::get, 203 types, 204 metadataUtil)); 205 206 if (usesRawFrameworkTypes) { 207 injectMembersBuilder.addAnnotation(suppressWarnings(UNCHECKED)); 208 } 209 injectorTypeBuilder.addMethod(injectMembersBuilder.build()); 210 211 for (InjectionSite injectionSite : binding.injectionSites()) { 212 if (injectionSite.element().getEnclosingElement().equals(binding.membersInjectedType())) { 213 injectorTypeBuilder.addMethod(InjectionSiteMethod.create(injectionSite, metadataUtil)); 214 } 215 } 216 217 gwtIncompatibleAnnotation(binding).ifPresent(injectorTypeBuilder::addAnnotation); 218 219 return ImmutableList.of(injectorTypeBuilder); 220 } 221 } 222