• 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 androidx.room.compiler.processing.XElementKt.isMethod;
20 import static androidx.room.compiler.processing.XTypeKt.isVoid;
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.base.Preconditions.checkNotNull;
23 import static com.google.common.base.Preconditions.checkState;
24 import static com.google.common.collect.Iterables.getOnlyElement;
25 import static dagger.internal.codegen.base.ComponentAnnotation.rootComponentAnnotation;
26 import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotation;
27 import static dagger.internal.codegen.base.ComponentAnnotation.subcomponentAnnotations;
28 import static dagger.internal.codegen.base.ComponentCreatorAnnotation.creatorAnnotationsFor;
29 import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
30 import static dagger.internal.codegen.base.Scopes.productionScope;
31 import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
32 import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes;
33 import static dagger.internal.codegen.binding.ConfigurationAnnotations.isSubcomponentCreator;
34 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
35 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
36 import static dagger.internal.codegen.javapoet.TypeNames.isFutureType;
37 import static dagger.internal.codegen.xprocessing.XElements.getSimpleName;
38 import static dagger.internal.codegen.xprocessing.XTypeElements.getAllUnimplementedMethods;
39 import static dagger.internal.codegen.xprocessing.XTypes.isDeclared;
40 
41 import androidx.room.compiler.processing.XElement;
42 import androidx.room.compiler.processing.XMethodElement;
43 import androidx.room.compiler.processing.XMethodType;
44 import androidx.room.compiler.processing.XProcessingEnv;
45 import androidx.room.compiler.processing.XType;
46 import androidx.room.compiler.processing.XTypeElement;
47 import com.google.auto.value.AutoValue;
48 import com.google.auto.value.extension.memoized.Memoized;
49 import com.google.common.base.Supplier;
50 import com.google.common.base.Suppliers;
51 import com.google.common.collect.HashMultimap;
52 import com.google.common.collect.ImmutableBiMap;
53 import com.google.common.collect.ImmutableMap;
54 import com.google.common.collect.ImmutableSet;
55 import com.google.common.collect.Maps;
56 import com.google.errorprone.annotations.CanIgnoreReturnValue;
57 import com.google.errorprone.annotations.CheckReturnValue;
58 import com.squareup.javapoet.TypeName;
59 import dagger.Component;
60 import dagger.Module;
61 import dagger.Subcomponent;
62 import dagger.internal.codegen.base.ClearableCache;
63 import dagger.internal.codegen.base.ComponentAnnotation;
64 import dagger.internal.codegen.base.DaggerSuperficialValidation;
65 import dagger.internal.codegen.base.ModuleAnnotation;
66 import dagger.internal.codegen.javapoet.TypeNames;
67 import dagger.internal.codegen.model.DependencyRequest;
68 import dagger.internal.codegen.model.Scope;
69 import dagger.internal.codegen.xprocessing.XTypeElements;
70 import java.util.HashMap;
71 import java.util.Map;
72 import java.util.Objects;
73 import java.util.Optional;
74 import java.util.stream.Stream;
75 import javax.inject.Inject;
76 import javax.inject.Singleton;
77 
78 /**
79  * A component declaration.
80  *
81  * <p>Represents one type annotated with {@code @Component}, {@code Subcomponent},
82  * {@code @ProductionComponent}, or {@code @ProductionSubcomponent}.
83  *
84  * <p>When validating bindings installed in modules, a {@link ComponentDescriptor} can also
85  * represent a synthetic component for the module, where there is an entry point for each binding in
86  * the module.
87  */
88 @CheckReturnValue
89 @AutoValue
90 public abstract class ComponentDescriptor {
91   private BindingFactory bindingFactory;
92 
93   /** The annotation that specifies that {@link #typeElement()} is a component. */
annotation()94   public abstract ComponentAnnotation annotation();
95 
96   /**
97    * The element that defines the component. This is the element to which the {@link #annotation()}
98    * was applied.
99    */
typeElement()100   public abstract XTypeElement typeElement();
101 
102   /**
103    * The set of component dependencies listed in {@link Component#dependencies} or {@link
104    * dagger.producers.ProductionComponent#dependencies()}.
105    */
dependencies()106   public abstract ImmutableSet<ComponentRequirement> dependencies();
107 
108   /**
109    * The {@link ModuleDescriptor modules} declared in {@link Component#modules()} and reachable by
110    * traversing {@link Module#includes()}.
111    */
modules()112   public abstract ImmutableSet<ModuleDescriptor> modules();
113 
114   /** The scopes of the component. */
scopes()115   public abstract ImmutableSet<Scope> scopes();
116 
117   /**
118    * All {@linkplain Subcomponent direct child} components that are declared by a {@linkplain
119    * Module#subcomponents() module's subcomponents}.
120    */
childComponentsDeclaredByModules()121   abstract ImmutableSet<ComponentDescriptor> childComponentsDeclaredByModules();
122 
123   /**
124    * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
125    * factory method.
126    */
127   public abstract ImmutableBiMap<ComponentMethodDescriptor, ComponentDescriptor>
childComponentsDeclaredByFactoryMethods()128       childComponentsDeclaredByFactoryMethods();
129 
130   /**
131    * All {@linkplain Subcomponent direct child} components that are declared by a subcomponent
132    * builder method.
133    */
134   abstract ImmutableMap<ComponentMethodDescriptor, ComponentDescriptor>
childComponentsDeclaredByBuilderEntryPoints()135       childComponentsDeclaredByBuilderEntryPoints();
136 
componentMethods()137   public abstract ImmutableSet<ComponentMethodDescriptor> componentMethods();
138 
139   /** Returns a descriptor for the creator type for this component type, if the user defined one. */
creatorDescriptor()140   public abstract Optional<ComponentCreatorDescriptor> creatorDescriptor();
141 
142   /** Returns {@code true} if this is a subcomponent. */
isSubcomponent()143   public final boolean isSubcomponent() {
144     return annotation().isSubcomponent();
145   }
146 
147   /**
148    * Returns {@code true} if this is a production component or subcomponent, or a
149    * {@code @ProducerModule} when doing module binding validation.
150    */
isProduction()151   public final boolean isProduction() {
152     return annotation().isProduction();
153   }
154 
155   /**
156    * Returns {@code true} if this is a real component, and not a fictional one used to validate
157    * module bindings.
158    */
isRealComponent()159   public final boolean isRealComponent() {
160     return annotation().isRealComponent();
161   }
162 
163   /** The non-abstract {@link #modules()} and the {@link #dependencies()}. */
dependenciesAndConcreteModules()164   public final ImmutableSet<ComponentRequirement> dependenciesAndConcreteModules() {
165     return Stream.concat(
166             moduleTypes().stream()
167                 .filter(dep -> !dep.isAbstract())
168                 .map(module -> ComponentRequirement.forModule(module.getType())),
169             dependencies().stream())
170         .collect(toImmutableSet());
171   }
172 
173   /** The types of the {@link #modules()}. */
moduleTypes()174   public final ImmutableSet<XTypeElement> moduleTypes() {
175     return modules().stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
176   }
177 
178   /**
179    * The types for which the component will need instances if all of its bindings are used. For the
180    * types the component will need in a given binding graph, use {@link
181    * BindingGraph#componentRequirements()}.
182    *
183    * <ul>
184    *   <li>{@linkplain #modules()} modules} with concrete instance bindings
185    *   <li>Bound instances
186    *   <li>{@linkplain #dependencies() dependencies}
187    * </ul>
188    */
189   @Memoized
requirements()190   ImmutableSet<ComponentRequirement> requirements() {
191     ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
192     modules().stream()
193         .filter(
194             module ->
195                 module.bindings().stream().anyMatch(ContributionBinding::requiresModuleInstance))
196         .map(module -> ComponentRequirement.forModule(module.moduleElement().getType()))
197         .forEach(requirements::add);
198     requirements.addAll(dependencies());
199     requirements.addAll(
200         creatorDescriptor()
201             .map(ComponentCreatorDescriptor::boundInstanceRequirements)
202             .orElse(ImmutableSet.of()));
203     return requirements.build();
204   }
205 
206   /**
207    * Returns this component's dependencies keyed by its provision/production method.
208    *
209    * <p>Note that the dependencies' types are not simply the enclosing type of the method; a method
210    * may be declared by a supertype of the actual dependency.
211    */
212   @Memoized
dependenciesByDependencyMethod()213   public ImmutableMap<XMethodElement, ComponentRequirement> dependenciesByDependencyMethod() {
214     ImmutableMap.Builder<XMethodElement, ComponentRequirement> builder = ImmutableMap.builder();
215     for (ComponentRequirement componentDependency : dependencies()) {
216       XTypeElements.getAllMethods(componentDependency.typeElement()).stream()
217           .filter(ComponentDescriptor::isComponentContributionMethod)
218           .forEach(method -> builder.put(method, componentDependency));
219     }
220     return builder.buildOrThrow();
221   }
222 
223   /** The {@linkplain #dependencies() component dependency} that defines a method. */
getDependencyThatDefinesMethod(XElement method)224   public final ComponentRequirement getDependencyThatDefinesMethod(XElement method) {
225     checkArgument(isMethod(method), "method must be an executable element: %s", method);
226     checkState(
227         dependenciesByDependencyMethod().containsKey(method),
228         "no dependency implements %s",
229         method);
230     return dependenciesByDependencyMethod().get(method);
231   }
232 
233   /**
234    * All {@link Subcomponent}s which are direct children of this component. This includes
235    * subcomponents installed from {@link Module#subcomponents()} as well as subcomponent {@linkplain
236    * #childComponentsDeclaredByFactoryMethods() factory methods} and {@linkplain
237    * #childComponentsDeclaredByBuilderEntryPoints() builder methods}.
238    */
childComponents()239   public final ImmutableSet<ComponentDescriptor> childComponents() {
240     return ImmutableSet.<ComponentDescriptor>builder()
241         .addAll(childComponentsDeclaredByFactoryMethods().values())
242         .addAll(childComponentsDeclaredByBuilderEntryPoints().values())
243         .addAll(childComponentsDeclaredByModules())
244         .build();
245   }
246 
247   /** Returns a map of {@link #childComponents()} indexed by {@link #typeElement()}. */
248   @Memoized
childComponentsByElement()249   public ImmutableMap<XTypeElement, ComponentDescriptor> childComponentsByElement() {
250     return Maps.uniqueIndex(childComponents(), ComponentDescriptor::typeElement);
251   }
252 
253   /** Returns the factory method that declares a child component. */
getFactoryMethodForChildComponent( ComponentDescriptor childComponent)254   final Optional<ComponentMethodDescriptor> getFactoryMethodForChildComponent(
255       ComponentDescriptor childComponent) {
256     return Optional.ofNullable(
257         childComponentsDeclaredByFactoryMethods().inverse().get(childComponent));
258   }
259 
260   private final Supplier<ImmutableMap<XTypeElement, ComponentDescriptor>>
261       childComponentsByBuilderType =
262           Suppliers.memoize(
263               () ->
264                   childComponents().stream()
265                       .filter(child -> child.creatorDescriptor().isPresent())
266                       .collect(
267                           toImmutableMap(
268                               child -> child.creatorDescriptor().get().typeElement(),
269                               child -> child)));
270 
271   /** Returns the child component with the given builder type. */
getChildComponentWithBuilderType(XTypeElement builderType)272   final ComponentDescriptor getChildComponentWithBuilderType(XTypeElement builderType) {
273     return checkNotNull(
274         childComponentsByBuilderType.get().get(builderType),
275         "no child component found for builder type %s",
276         builderType.getQualifiedName());
277   }
278 
279   /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */
entryPointMethods()280   public final ImmutableSet<ComponentMethodDescriptor> entryPointMethods() {
281     return componentMethods().stream()
282         .filter(method -> method.dependencyRequest().isPresent())
283         .collect(toImmutableSet());
284   }
285 
286   /**
287    * Returns {@code true} for components that have a creator, either because the user {@linkplain
288    * #creatorDescriptor() specified one} or because it's a top-level component with an implicit
289    * builder.
290    */
hasCreator()291   public final boolean hasCreator() {
292     return !isSubcomponent() || creatorDescriptor().isPresent();
293   }
294 
295   /**
296    * Returns the {@link CancellationPolicy} for this component, or an empty optional if either the
297    * component is not a production component or no {@code CancellationPolicy} annotation is present.
298    */
cancellationPolicy()299   public final Optional<CancellationPolicy> cancellationPolicy() {
300     return isProduction()
301         // TODO(bcorso): Get values from XAnnotation instead of using CancellationPolicy directly.
302         ? Optional.ofNullable(typeElement().getAnnotation(TypeNames.CANCELLATION_POLICY))
303             .map(CancellationPolicy::from)
304         : Optional.empty();
305   }
306 
307   /** Returns the bindings for the component. */
308   @Memoized
bindings()309   public ImmutableSet<ContributionBinding> bindings() {
310     ImmutableSet.Builder<ContributionBinding> builder = ImmutableSet.builder();
311     componentBinding().ifPresent(builder::add);
312     return builder
313         .addAll(componentDependencyBindings())
314         .addAll(boundInstanceBindings())
315         .addAll(subcomponentCreatorBindings())
316         .addAll(moduleBindings())
317         .build();
318   }
319 
320   /** Returns the binding for the component, itself, if this is a real component. */
321   @Memoized
componentBinding()322   Optional<ContributionBinding> componentBinding() {
323     return isRealComponent()
324         ? Optional.of(bindingFactory.componentBinding(typeElement()))
325         : Optional.empty();
326   }
327 
328   /** Returns the bindings for the component dependency and those contributed by its methods. */
329   @Memoized
componentDependencyBindings()330   ImmutableSet<ContributionBinding> componentDependencyBindings() {
331     ImmutableSet.Builder<ContributionBinding> builder = ImmutableSet.builder();
332     for (ComponentRequirement dependency : dependencies()) {
333       builder.add(bindingFactory.componentDependencyBinding(dependency));
334 
335       // Within a component dependency, we want to allow the same method to appear multiple
336       // times assuming it is the exact same method. We do this by tracking a set of bindings
337       // we've already added with the binding element removed since that is the only thing
338       // allowed to differ.
339       HashMultimap<String, ContributionBinding> dedupeBindings = HashMultimap.create();
340       XTypeElements.getAllMethods(dependency.typeElement()).stream()
341           // MembersInjection methods aren't "provided" explicitly, so ignore them.
342           .filter(ComponentDescriptor::isComponentContributionMethod)
343           .forEach(
344               method -> {
345                 ContributionBinding binding =
346                     isProduction() && isComponentProductionMethod(method)
347                         ? bindingFactory.componentDependencyProductionMethodBinding(method)
348                         : bindingFactory.componentDependencyProvisionMethodBinding(method);
349                 if (dedupeBindings.put(
350                     getSimpleName(method),
351                     // Remove the binding element since we know that will be different, but
352                     // everything else we want to be the same to consider it a duplicate.
353                     binding.toBuilder().clearBindingElement().build())) {
354                   builder.add(binding);
355                 }
356               });
357     }
358     return builder.build();
359   }
360 
361   /** Returns the {@code @BindsInstance} bindings required to create this component. */
362   @Memoized
boundInstanceBindings()363   ImmutableSet<ContributionBinding> boundInstanceBindings() {
364     return creatorDescriptor().isPresent()
365         ? creatorDescriptor().get().boundInstanceRequirements().stream()
366             .map(
367                 requirement ->
368                     bindingFactory.boundInstanceBinding(
369                         requirement,
370                         creatorDescriptor().get().elementForRequirement(requirement)))
371             .collect(toImmutableSet())
372         : ImmutableSet.of();
373   }
374 
375   /** Returns the subcomponent creator bindings for this component. */
376   @Memoized
subcomponentCreatorBindings()377   ImmutableSet<ContributionBinding> subcomponentCreatorBindings() {
378     ImmutableSet.Builder<ContributionBinding> builder = ImmutableSet.builder();
379     childComponentsDeclaredByBuilderEntryPoints()
380         .forEach(
381             (builderEntryPoint, childComponent) -> {
382               if (!childComponentsDeclaredByModules().contains(childComponent)) {
383                 builder.add(
384                     bindingFactory.subcomponentCreatorBinding(
385                         builderEntryPoint.methodElement(), typeElement()));
386               }
387             });
388     return builder.build();
389   }
390 
391   @Memoized
moduleBindings()392   ImmutableSet<ContributionBinding> moduleBindings() {
393     return modules().stream()
394         .map(ModuleDescriptor::bindings)
395         .flatMap(ImmutableSet::stream)
396         .collect(toImmutableSet());
397   }
398 
399   @Memoized
delegateDeclarations()400   public ImmutableSet<DelegateDeclaration> delegateDeclarations() {
401     return modules().stream()
402         .map(ModuleDescriptor::delegateDeclarations)
403         .flatMap(ImmutableSet::stream)
404         .collect(toImmutableSet());
405   }
406 
407   @Memoized
multibindingDeclarations()408   public ImmutableSet<MultibindingDeclaration> multibindingDeclarations() {
409     return modules().stream()
410         .map(ModuleDescriptor::multibindingDeclarations)
411         .flatMap(ImmutableSet::stream)
412         .collect(toImmutableSet());
413   }
414 
415   @Memoized
optionalBindingDeclarations()416   public ImmutableSet<OptionalBindingDeclaration> optionalBindingDeclarations() {
417     return modules().stream()
418         .map(ModuleDescriptor::optionalDeclarations)
419         .flatMap(ImmutableSet::stream)
420         .collect(toImmutableSet());
421   }
422 
423   @Memoized
subcomponentDeclarations()424   public ImmutableSet<SubcomponentDeclaration> subcomponentDeclarations() {
425     return modules().stream()
426         .map(ModuleDescriptor::subcomponentDeclarations)
427         .flatMap(ImmutableSet::stream)
428         .collect(toImmutableSet());
429   }
430 
431   @Memoized
432   @Override
hashCode()433   public int hashCode() {
434     // TODO(b/122962745): Only use typeElement().hashCode()
435     return Objects.hash(typeElement(), annotation());
436   }
437 
438   // TODO(ronshapiro): simplify the equality semantics
439   @Override
equals(Object obj)440   public abstract boolean equals(Object obj);
441 
442   /** A component method. */
443   @AutoValue
444   public abstract static class ComponentMethodDescriptor {
445     /** The method itself. Note that this may be declared on a supertype of the component. */
methodElement()446     public abstract XMethodElement methodElement();
447 
448     /**
449      * The dependency request for production, provision, and subcomponent creator methods. Absent
450      * for subcomponent factory methods.
451      */
dependencyRequest()452     public abstract Optional<DependencyRequest> dependencyRequest();
453 
454     /** The subcomponent for subcomponent factory methods and subcomponent creator methods. */
subcomponent()455     public abstract Optional<ComponentDescriptor> subcomponent();
456 
457     /** A {@link ComponentMethodDescriptor}builder for a method. */
builder(XMethodElement method)458     public static Builder builder(XMethodElement method) {
459       return new AutoValue_ComponentDescriptor_ComponentMethodDescriptor.Builder()
460           .methodElement(method);
461     }
462 
463     /** A builder of {@link ComponentMethodDescriptor}s. */
464     @AutoValue.Builder
465     public interface Builder {
466       /** @see ComponentMethodDescriptor#methodElement() */
methodElement(XMethodElement methodElement)467       Builder methodElement(XMethodElement methodElement);
468 
469       /**
470        * @see ComponentMethodDescriptor#dependencyRequest()
471        */
472       @CanIgnoreReturnValue // TODO(kak): remove this once open-source checkers understand AutoValue
dependencyRequest(DependencyRequest dependencyRequest)473       Builder dependencyRequest(DependencyRequest dependencyRequest);
474 
475       /**
476        * @see ComponentMethodDescriptor#subcomponent()
477        */
478       @CanIgnoreReturnValue // TODO(kak): remove this once open-source checkers understand AutoValue
subcomponent(ComponentDescriptor subcomponent)479       Builder subcomponent(ComponentDescriptor subcomponent);
480 
481       /** Builds the descriptor. */
build()482       ComponentMethodDescriptor build();
483     }
484   }
485 
486   /** No-argument methods defined on {@link Object} that are ignored for contribution. */
487   private static final ImmutableSet<String> NON_CONTRIBUTING_OBJECT_METHOD_NAMES =
488       ImmutableSet.of("toString", "hashCode", "clone", "getClass");
489 
490   /**
491    * Returns {@code true} if a method could be a component entry point but not a members-injection
492    * method.
493    */
isComponentContributionMethod(XMethodElement method)494   private static boolean isComponentContributionMethod(XMethodElement method) {
495     return method.getParameters().isEmpty()
496         && !isVoid(method.getReturnType())
497         && !method.getEnclosingElement().getClassName().equals(TypeName.OBJECT)
498         && !NON_CONTRIBUTING_OBJECT_METHOD_NAMES.contains(getSimpleName(method));
499   }
500 
501   /** Returns {@code true} if a method could be a component production entry point. */
isComponentProductionMethod(XMethodElement method)502   private static boolean isComponentProductionMethod(XMethodElement method) {
503     return isComponentContributionMethod(method) && isFutureType(method.getReturnType());
504   }
505 
506   /** A factory for creating a {@link ComponentDescriptor}. */
507   @Singleton
508   public static final class Factory implements ClearableCache {
509     private final XProcessingEnv processingEnv;
510     private final BindingFactory bindingFactory;
511     private final DependencyRequestFactory dependencyRequestFactory;
512     private final ModuleDescriptor.Factory moduleDescriptorFactory;
513     private final InjectionAnnotations injectionAnnotations;
514     private final DaggerSuperficialValidation superficialValidation;
515     private final Map<XTypeElement, ComponentDescriptor> cache = new HashMap<>();
516 
517     @Inject
Factory( XProcessingEnv processingEnv, BindingFactory bindingFactory, DependencyRequestFactory dependencyRequestFactory, ModuleDescriptor.Factory moduleDescriptorFactory, InjectionAnnotations injectionAnnotations, DaggerSuperficialValidation superficialValidation)518     Factory(
519         XProcessingEnv processingEnv,
520         BindingFactory bindingFactory,
521         DependencyRequestFactory dependencyRequestFactory,
522         ModuleDescriptor.Factory moduleDescriptorFactory,
523         InjectionAnnotations injectionAnnotations,
524         DaggerSuperficialValidation superficialValidation) {
525       this.processingEnv = processingEnv;
526       this.bindingFactory = bindingFactory;
527       this.dependencyRequestFactory = dependencyRequestFactory;
528       this.moduleDescriptorFactory = moduleDescriptorFactory;
529       this.injectionAnnotations = injectionAnnotations;
530       this.superficialValidation = superficialValidation;
531     }
532 
533     /** Returns a descriptor for a root component type. */
rootComponentDescriptor(XTypeElement typeElement)534     public ComponentDescriptor rootComponentDescriptor(XTypeElement typeElement) {
535       Optional<ComponentAnnotation> annotation =
536           rootComponentAnnotation(typeElement, superficialValidation);
537       checkArgument(annotation.isPresent(), "%s must have a component annotation", typeElement);
538       return create(typeElement, annotation.get());
539     }
540 
541     /** Returns a descriptor for a subcomponent type. */
subcomponentDescriptor(XTypeElement typeElement)542     public ComponentDescriptor subcomponentDescriptor(XTypeElement typeElement) {
543       Optional<ComponentAnnotation> annotation =
544           subcomponentAnnotation(typeElement, superficialValidation);
545       checkArgument(annotation.isPresent(), "%s must have a subcomponent annotation", typeElement);
546       return create(typeElement, annotation.get());
547     }
548 
549     /**
550      * Returns a descriptor for a fictional component based on a module type in order to validate
551      * its bindings.
552      */
moduleComponentDescriptor(XTypeElement typeElement)553     public ComponentDescriptor moduleComponentDescriptor(XTypeElement typeElement) {
554       Optional<ModuleAnnotation> annotation = moduleAnnotation(typeElement, superficialValidation);
555       checkArgument(annotation.isPresent(), "%s must have a module annotation", typeElement);
556       return create(typeElement, ComponentAnnotation.fromModuleAnnotation(annotation.get()));
557     }
558 
create( XTypeElement typeElement, ComponentAnnotation componentAnnotation)559     private ComponentDescriptor create(
560         XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
561       return reentrantComputeIfAbsent(
562           cache, typeElement, unused -> createUncached(typeElement, componentAnnotation));
563     }
564 
createUncached( XTypeElement typeElement, ComponentAnnotation componentAnnotation)565     private ComponentDescriptor createUncached(
566         XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
567       ImmutableSet<ComponentRequirement> componentDependencies =
568           componentAnnotation.dependencyTypes().stream()
569               .map(ComponentRequirement::forDependency)
570               .collect(toImmutableSet());
571 
572       // Start with the component's modules. For fictional components built from a module, start
573       // with that module.
574       ImmutableSet<XTypeElement> modules =
575           componentAnnotation.isRealComponent()
576               ? componentAnnotation.modules()
577               : ImmutableSet.of(typeElement);
578 
579       ImmutableSet<ModuleDescriptor> transitiveModules =
580           moduleDescriptorFactory.transitiveModules(modules);
581 
582       ImmutableSet.Builder<ComponentMethodDescriptor> componentMethodsBuilder =
583           ImmutableSet.builder();
584       ImmutableBiMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
585           subcomponentsByFactoryMethod = ImmutableBiMap.builder();
586       ImmutableMap.Builder<ComponentMethodDescriptor, ComponentDescriptor>
587           subcomponentsByBuilderMethod = ImmutableBiMap.builder();
588       if (componentAnnotation.isRealComponent()) {
589         for (XMethodElement componentMethod : getAllUnimplementedMethods(typeElement)) {
590           ComponentMethodDescriptor componentMethodDescriptor =
591               getDescriptorForComponentMethod(componentAnnotation, typeElement, componentMethod);
592           componentMethodsBuilder.add(componentMethodDescriptor);
593           componentMethodDescriptor
594               .subcomponent()
595               .ifPresent(
596                   subcomponent -> {
597                     // If the dependency request is present, that means the method returns the
598                     // subcomponent factory.
599                     if (componentMethodDescriptor.dependencyRequest().isPresent()) {
600                       subcomponentsByBuilderMethod.put(componentMethodDescriptor, subcomponent);
601                     } else {
602                       subcomponentsByFactoryMethod.put(componentMethodDescriptor, subcomponent);
603                     }
604                   });
605         }
606       }
607 
608       // Validation should have ensured that this set will have at most one element.
609       ImmutableSet<XTypeElement> enclosedCreators =
610           enclosedAnnotatedTypes(typeElement, creatorAnnotationsFor(componentAnnotation));
611       Optional<ComponentCreatorDescriptor> creatorDescriptor =
612           enclosedCreators.isEmpty()
613               ? Optional.empty()
614               : Optional.of(
615                   ComponentCreatorDescriptor.create(
616                       getOnlyElement(enclosedCreators), dependencyRequestFactory));
617 
618       ImmutableSet<Scope> scopes = injectionAnnotations.getScopes(typeElement);
619       if (componentAnnotation.isProduction()) {
620         scopes =
621             ImmutableSet.<Scope>builder()
622                 .addAll(scopes).add(productionScope(processingEnv))
623                 .build();
624       }
625 
626       ImmutableSet<ComponentDescriptor> subcomponentsFromModules =
627         transitiveModules.stream()
628             .flatMap(transitiveModule -> transitiveModule.subcomponentDeclarations().stream())
629             .map(SubcomponentDeclaration::subcomponentType)
630             .map(this::subcomponentDescriptor)
631             .collect(toImmutableSet());
632 
633       ComponentDescriptor componentDescriptor =
634           new AutoValue_ComponentDescriptor(
635               componentAnnotation,
636               typeElement,
637               componentDependencies,
638               transitiveModules,
639               scopes,
640               subcomponentsFromModules,
641               subcomponentsByFactoryMethod.buildOrThrow(),
642               subcomponentsByBuilderMethod.buildOrThrow(),
643               componentMethodsBuilder.build(),
644               creatorDescriptor);
645       componentDescriptor.bindingFactory = bindingFactory;
646       return componentDescriptor;
647     }
648 
getDescriptorForComponentMethod( ComponentAnnotation componentAnnotation, XTypeElement componentElement, XMethodElement componentMethod)649     private ComponentMethodDescriptor getDescriptorForComponentMethod(
650         ComponentAnnotation componentAnnotation,
651         XTypeElement componentElement,
652         XMethodElement componentMethod) {
653       ComponentMethodDescriptor.Builder descriptor =
654           ComponentMethodDescriptor.builder(componentMethod);
655 
656       XMethodType resolvedComponentMethod = componentMethod.asMemberOf(componentElement.getType());
657       XType returnType = resolvedComponentMethod.getReturnType();
658       if (isDeclared(returnType)
659               && !injectionAnnotations.getQualifier(componentMethod).isPresent()) {
660         XTypeElement returnTypeElement = returnType.getTypeElement();
661         if (returnTypeElement.hasAnyAnnotation(subcomponentAnnotations())) {
662           // It's a subcomponent factory method. There is no dependency request, and there could be
663           // any number of parameters. Just return the descriptor.
664           return descriptor.subcomponent(subcomponentDescriptor(returnTypeElement)).build();
665         }
666         if (isSubcomponentCreator(returnTypeElement)) {
667           descriptor.subcomponent(
668               subcomponentDescriptor(returnTypeElement.getEnclosingTypeElement()));
669         }
670       }
671 
672       switch (componentMethod.getParameters().size()) {
673         case 0:
674           checkArgument(
675               !isVoid(returnType), "component method cannot be void: %s", componentMethod);
676           descriptor.dependencyRequest(
677               componentAnnotation.isProduction()
678                   ? dependencyRequestFactory.forComponentProductionMethod(
679                       componentMethod, resolvedComponentMethod)
680                   : dependencyRequestFactory.forComponentProvisionMethod(
681                       componentMethod, resolvedComponentMethod));
682           break;
683 
684         case 1:
685           checkArgument(
686               isVoid(returnType)
687                   // TODO(bcorso): Replace this with isSameType()?
688                   || returnType
689                       .getTypeName()
690                       .equals(resolvedComponentMethod.getParameterTypes().get(0).getTypeName()),
691               "members injection method must return void or parameter type: %s",
692               componentMethod);
693           descriptor.dependencyRequest(
694               dependencyRequestFactory.forComponentMembersInjectionMethod(
695                   componentMethod, resolvedComponentMethod));
696           break;
697 
698         default:
699           throw new IllegalArgumentException(
700               "component method has too many parameters: " + componentMethod);
701       }
702 
703       return descriptor.build();
704     }
705 
706     @Override
clearCache()707     public void clearCache() {
708       cache.clear();
709     }
710   }
711 }
712