• 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.auto.common.MoreElements.asType;
20 import static com.google.auto.common.MoreTypes.asTypeElement;
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.collect.Iterables.getOnlyElement;
23 import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotation;
24 import static dagger.internal.codegen.ComponentCreatorAnnotation.creatorAnnotationsFor;
25 import static dagger.internal.codegen.ComponentDescriptor.isComponentContributionMethod;
26 import static dagger.internal.codegen.ConfigurationAnnotations.enclosedAnnotatedTypes;
27 import static dagger.internal.codegen.ConfigurationAnnotations.isSubcomponentCreator;
28 import static dagger.internal.codegen.DaggerStreams.toImmutableSet;
29 import static dagger.internal.codegen.InjectionAnnotations.getQualifier;
30 import static dagger.internal.codegen.Scopes.productionScope;
31 import static dagger.internal.codegen.Scopes.scopesOf;
32 import static javax.lang.model.type.TypeKind.DECLARED;
33 import static javax.lang.model.type.TypeKind.VOID;
34 import static javax.lang.model.util.ElementFilter.methodsIn;
35 
36 import com.google.auto.common.MoreTypes;
37 import com.google.common.collect.ImmutableBiMap;
38 import com.google.common.collect.ImmutableMap;
39 import com.google.common.collect.ImmutableSet;
40 import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
41 import dagger.internal.codegen.langmodel.DaggerElements;
42 import dagger.internal.codegen.langmodel.DaggerTypes;
43 import dagger.model.Scope;
44 import java.util.Optional;
45 import java.util.function.Function;
46 import javax.inject.Inject;
47 import javax.lang.model.element.ExecutableElement;
48 import javax.lang.model.element.TypeElement;
49 import javax.lang.model.type.DeclaredType;
50 import javax.lang.model.type.ExecutableType;
51 import javax.lang.model.type.TypeMirror;
52 
53 /** A factory for {@link ComponentDescriptor}s. */
54 final class ComponentDescriptorFactory {
55   private final DaggerElements elements;
56   private final DaggerTypes types;
57   private final DependencyRequestFactory dependencyRequestFactory;
58   private final ModuleDescriptor.Factory moduleDescriptorFactory;
59 
60   @Inject
ComponentDescriptorFactory( DaggerElements elements, DaggerTypes types, DependencyRequestFactory dependencyRequestFactory, ModuleDescriptor.Factory moduleDescriptorFactory)61   ComponentDescriptorFactory(
62       DaggerElements elements,
63       DaggerTypes types,
64       DependencyRequestFactory dependencyRequestFactory,
65       ModuleDescriptor.Factory moduleDescriptorFactory) {
66     this.elements = elements;
67     this.types = types;
68     this.dependencyRequestFactory = dependencyRequestFactory;
69     this.moduleDescriptorFactory = moduleDescriptorFactory;
70   }
71 
72   /** Returns a descriptor for a root component type. */
rootComponentDescriptor(TypeElement typeElement)73   ComponentDescriptor rootComponentDescriptor(TypeElement typeElement) {
74     return create(
75         typeElement,
76         checkAnnotation(
77             typeElement,
78             ComponentAnnotation::rootComponentAnnotation,
79             "must have a component annotation"));
80   }
81 
82   /** Returns a descriptor for a subcomponent type. */
subcomponentDescriptor(TypeElement typeElement)83   ComponentDescriptor subcomponentDescriptor(TypeElement typeElement) {
84     return create(
85         typeElement,
86         checkAnnotation(
87             typeElement,
88             ComponentAnnotation::subcomponentAnnotation,
89             "must have a subcomponent annotation"));
90   }
91 
92   /**
93    * Returns a descriptor for a fictional component based on a module type in order to validate its
94    * bindings.
95    */
moduleComponentDescriptor(TypeElement typeElement)96   ComponentDescriptor moduleComponentDescriptor(TypeElement typeElement) {
97     return create(
98         typeElement,
99         ComponentAnnotation.fromModuleAnnotation(
100             checkAnnotation(
101                 typeElement, ModuleAnnotation::moduleAnnotation, "must have a module annotation")));
102   }
103 
checkAnnotation( TypeElement typeElement, Function<TypeElement, Optional<A>> annotationFunction, String message)104   private static <A> A checkAnnotation(
105       TypeElement typeElement,
106       Function<TypeElement, Optional<A>> annotationFunction,
107       String message) {
108     return annotationFunction
109         .apply(typeElement)
110         .orElseThrow(() -> new IllegalArgumentException(typeElement + " " + message));
111   }
112 
create( TypeElement typeElement, ComponentAnnotation componentAnnotation)113   private ComponentDescriptor create(
114       TypeElement typeElement, ComponentAnnotation componentAnnotation) {
115     ImmutableSet<ComponentRequirement> componentDependencies =
116         componentAnnotation.dependencyTypes().stream()
117             .map(ComponentRequirement::forDependency)
118             .collect(toImmutableSet());
119 
120     ImmutableMap.Builder<ExecutableElement, ComponentRequirement> dependenciesByDependencyMethod =
121         ImmutableMap.builder();
122 
123     for (ComponentRequirement componentDependency : componentDependencies) {
124       for (ExecutableElement dependencyMethod :
125           methodsIn(elements.getAllMembers(componentDependency.typeElement()))) {
126         if (isComponentContributionMethod(elements, dependencyMethod)) {
127           dependenciesByDependencyMethod.put(dependencyMethod, componentDependency);
128         }
129       }
130     }
131 
132     // Start with the component's modules. For fictional components built from a module, start with
133     // that module.
134     ImmutableSet<TypeElement> modules =
135         componentAnnotation.isRealComponent()
136             ? componentAnnotation.modules()
137             : ImmutableSet.of(typeElement);
138 
139     ImmutableSet<ModuleDescriptor> transitiveModules =
140         moduleDescriptorFactory.transitiveModules(modules);
141 
142     ImmutableSet.Builder<ComponentDescriptor> subcomponentsFromModules = ImmutableSet.builder();
143     for (ModuleDescriptor module : transitiveModules) {
144       for (SubcomponentDeclaration subcomponentDeclaration : module.subcomponentDeclarations()) {
145         TypeElement subcomponent = subcomponentDeclaration.subcomponentType();
146         subcomponentsFromModules.add(subcomponentDescriptor(subcomponent));
147       }
148     }
149 
150     ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
151         ImmutableSet.builder();
152     ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
153         subcomponentsByFactoryMethod = ImmutableBiMap.builder();
154     ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
155         subcomponentsByBuilderMethod = ImmutableBiMap.builder();
156     if (componentAnnotation.isRealComponent()) {
157       ImmutableSet<ExecutableElement> unimplementedMethods =
158           elements.getUnimplementedMethods(typeElement);
159       for (ExecutableElement componentMethod : unimplementedMethods) {
160         ComponentMethodDescriptor componentMethodDescriptor =
161             getDescriptorForComponentMethod(typeElement, componentAnnotation, componentMethod);
162         componentMethodsBuilder.add(componentMethodDescriptor);
163         componentMethodDescriptor
164             .subcomponent()
165             .ifPresent(
166                 subcomponent -> {
167                   // If the dependency request is present, that means the method returns the
168                   // subcomponent factory.
169                   if (componentMethodDescriptor.dependencyRequest().isPresent()) {
170                     subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
171                   } else {
172                     subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
173                   }
174                 });
175       }
176     }
177 
178     // Validation should have ensured that this set will have at most one element.
179     ImmutableSet<DeclaredType> enclosedCreators =
180         creatorAnnotationsFor(componentAnnotation).stream()
181             .flatMap(
182                 creatorAnnotation ->
183                     enclosedAnnotatedTypes(typeElement, creatorAnnotation).stream())
184             .collect(toImmutableSet());
185     Optional<ComponentCreatorDescriptor> creatorDescriptor =
186         enclosedCreators.isEmpty()
187             ? Optional.empty()
188             : Optional.of(
189                 ComponentCreatorDescriptor.create(
190                     getOnlyElement(enclosedCreators), elements, types, dependencyRequestFactory));
191 
192     ImmutableSet<Scope> scopes = scopesOf(typeElement);
193     if (componentAnnotation.isProduction()) {
194       scopes = ImmutableSet.<Scope>builder().addAll(scopes).add(productionScope(elements)).build();
195     }
196 
197     return new AutoValue_ComponentDescriptor(
198         componentAnnotation,
199         typeElement,
200         componentDependencies,
201         transitiveModules,
202         dependenciesByDependencyMethod.build(),
203         scopes,
204         subcomponentsFromModules.build(),
205         subcomponentsByFactoryMethod.build(),
206         subcomponentsByBuilderMethod.build(),
207         componentMethodsBuilder.build(),
208         creatorDescriptor);
209   }
210 
getDescriptorForComponentMethod( TypeElement componentElement, ComponentAnnotation componentAnnotation, ExecutableElement componentMethod)211   private ComponentMethodDescriptor getDescriptorForComponentMethod(
212       TypeElement componentElement,
213       ComponentAnnotation componentAnnotation,
214       ExecutableElement componentMethod) {
215     ComponentMethodDescriptor.Builder descriptor =
216         ComponentMethodDescriptor.builder(componentMethod);
217 
218     ExecutableType resolvedComponentMethod =
219         MoreTypes.asExecutable(
220             types.asMemberOf(MoreTypes.asDeclared(componentElement.asType()), componentMethod));
221     TypeMirror returnType = resolvedComponentMethod.getReturnType();
222     if (returnType.getKind().equals(DECLARED) && !getQualifier(componentMethod).isPresent()) {
223       TypeElement returnTypeElement = asTypeElement(returnType);
224       if (subcomponentAnnotation(returnTypeElement).isPresent()) {
225         // It's a subcomponent factory method. There is no dependency request, and there could be
226         // any number of parameters. Just return the descriptor.
227         return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
228       }
229       if (isSubcomponentCreator(returnTypeElement)) {
230         descriptor.subcomponent(
231             subcomponentDescriptor(asType(returnTypeElement.getEnclosingElement())));
232       }
233     }
234 
235     switch (componentMethod.getParameters().size()) {
236       case 0:
237         checkArgument(
238             !returnType.getKind().equals(VOID),
239             "component method cannot be void: %s",
240             componentMethod);
241         descriptor.dependencyRequest(
242             componentAnnotation.isProduction()
243                 ? dependencyRequestFactory.forComponentProductionMethod(
244                     componentMethod, resolvedComponentMethod)
245                 : dependencyRequestFactory.forComponentProvisionMethod(
246                     componentMethod, resolvedComponentMethod));
247         break;
248 
249       case 1:
250         checkArgument(
251             returnType.getKind().equals(VOID)
252                 || MoreTypes.equivalence()
253                     .equivalent(returnType, resolvedComponentMethod.getParameterTypes().get(0)),
254             "members injection method must return void or parameter type: %s",
255             componentMethod);
256         descriptor.dependencyRequest(
257             dependencyRequestFactory.forComponentMembersInjectionMethod(
258                 componentMethod, resolvedComponentMethod));
259         break;
260 
261       default:
262         throw new IllegalArgumentException(
263             "component method has too many parameters: " + componentMethod);
264     }
265 
266     return descriptor.build();
267   }
268 }
269