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