• 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.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