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