• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.common.base.Preconditions.checkState;
20 import static dagger.internal.codegen.ComponentGenerator.componentName;
21 import static dagger.internal.codegen.Util.reentrantComputeIfAbsent;
22 import static javax.tools.Diagnostic.Kind.WARNING;
23 
24 import com.squareup.javapoet.ClassName;
25 import dagger.internal.codegen.langmodel.DaggerElements;
26 import dagger.internal.codegen.serialization.ProtoSerialization.InconsistentSerializedProtoException;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.Optional;
30 import javax.annotation.processing.Messager;
31 import javax.inject.Inject;
32 import javax.inject.Singleton;
33 import javax.lang.model.element.TypeElement;
34 
35 /** Factory for {@link ComponentImplementation}s. */
36 @Singleton
37 final class ComponentImplementationFactory implements ClearableCache {
38   private final Map<TypeElement, ComponentImplementation> topLevelComponentCache = new HashMap<>();
39   private final KeyFactory keyFactory;
40   private final CompilerOptions compilerOptions;
41   private final BindingGraphFactory bindingGraphFactory;
42   private final TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder;
43   private final DeserializedComponentImplementationBuilder
44       deserializedComponentImplementationBuilder;
45   private final DaggerElements elements;
46   private final Messager messager;
47 
48   @Inject
ComponentImplementationFactory( KeyFactory keyFactory, CompilerOptions compilerOptions, BindingGraphFactory bindingGraphFactory, TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder, DeserializedComponentImplementationBuilder deserializedComponentImplementationBuilder, DaggerElements elements, Messager messager)49   ComponentImplementationFactory(
50       KeyFactory keyFactory,
51       CompilerOptions compilerOptions,
52       BindingGraphFactory bindingGraphFactory,
53       TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder,
54       DeserializedComponentImplementationBuilder deserializedComponentImplementationBuilder,
55       DaggerElements elements,
56       Messager messager) {
57     this.keyFactory = keyFactory;
58     this.compilerOptions = compilerOptions;
59     this.bindingGraphFactory = bindingGraphFactory;
60     this.topLevelImplementationComponentBuilder = topLevelImplementationComponentBuilder;
61     this.deserializedComponentImplementationBuilder = deserializedComponentImplementationBuilder;
62     this.elements = elements;
63     this.messager = messager;
64   }
65 
66   /**
67    * Returns a top-level (non-nested) component implementation for a binding graph.
68    *
69    * @throws IllegalStateException if the binding graph is for a subcomponent and
70    *     ahead-of-time-subcomponents mode is not enabled
71    */
createComponentImplementation(BindingGraph bindingGraph)72   ComponentImplementation createComponentImplementation(BindingGraph bindingGraph) {
73     return reentrantComputeIfAbsent(
74         topLevelComponentCache,
75         bindingGraph.componentTypeElement(),
76         component -> createComponentImplementationUncached(bindingGraph));
77   }
78 
createComponentImplementationUncached(BindingGraph bindingGraph)79   private ComponentImplementation createComponentImplementationUncached(BindingGraph bindingGraph) {
80     ComponentImplementation componentImplementation =
81         ComponentImplementation.topLevelComponentImplementation(
82             bindingGraph,
83             componentName(bindingGraph.componentTypeElement()),
84             new SubcomponentNames(bindingGraph, keyFactory),
85             compilerOptions);
86 
87     // TODO(dpb): explore using optional bindings for the "parent" bindings
88     CurrentImplementationSubcomponent currentImplementationSubcomponent =
89         topLevelImplementationComponentBuilder
90             .topLevelComponent(componentImplementation)
91             .build()
92             .currentImplementationSubcomponentBuilder()
93             .componentImplementation(componentImplementation)
94             .bindingGraph(bindingGraph)
95             .parentBuilder(Optional.empty())
96             .parentBindingExpressions(Optional.empty())
97             .parentRequirementExpressions(Optional.empty())
98             .build();
99 
100     if (componentImplementation.isAbstract()) {
101       checkState(
102           compilerOptions.aheadOfTimeSubcomponents(),
103           "Calling 'componentImplementation()' on %s when not generating ahead-of-time "
104               + "subcomponents.",
105           bindingGraph.componentTypeElement());
106       return currentImplementationSubcomponent.subcomponentBuilder().build();
107     } else {
108       return currentImplementationSubcomponent.rootComponentBuilder().build();
109     }
110   }
111 
112   /** Returns the superclass of the child nested within a superclass of the parent component. */
findChildSuperclassImplementation( ComponentDescriptor child, ComponentImplementation parentImplementation)113   ComponentImplementation findChildSuperclassImplementation(
114       ComponentDescriptor child, ComponentImplementation parentImplementation) {
115     // If the current component has superclass implementations, a superclass may contain a
116     // reference to the child. Traverse this component's superimplementation hierarchy looking for
117     // the child's implementation. The child superclass implementation may not be present in the
118     // direct superclass implementations if the subcomponent builder was previously a pruned
119     // binding.
120     for (Optional<ComponentImplementation> parent = parentImplementation.superclassImplementation();
121         parent.isPresent();
122         parent = parent.get().superclassImplementation()) {
123       Optional<ComponentImplementation> superclass = parent.get().childImplementation(child);
124       if (superclass.isPresent()) {
125         return superclass.get();
126       }
127     }
128 
129     if (compilerOptions.emitModifiableMetadataAnnotations()) {
130       ClassName childSuperclassName = componentName(child.typeElement());
131       TypeElement generatedChildSuperclassImplementation =
132           elements.getTypeElement(childSuperclassName);
133       if (generatedChildSuperclassImplementation != null) {
134         try {
135           return deserializedComponentImplementationBuilder.create(
136               child, generatedChildSuperclassImplementation);
137         } catch (InconsistentSerializedProtoException e) {
138           messager.printMessage(
139               WARNING,
140               String.format(
141                   "%s was compiled with a different version of Dagger than the version in this "
142                       + "compilation. To ensure the validity of Dagger's generated code, compile "
143                       + "all Dagger code with the same version.",
144                   child.typeElement().getQualifiedName()));
145         }
146       } else if (compilerOptions.forceUseSerializedComponentImplementations()) {
147         throw new TypeNotPresentException(childSuperclassName.toString(), null);
148       }
149     }
150 
151     // Otherwise, the superclass implementation is top-level, so we must recreate the
152     // implementation object for the base implementation of the child by truncating the binding
153     // graph at the child.
154     BindingGraph truncatedBindingGraph = bindingGraphFactory.create(child, false);
155     return createComponentImplementation(truncatedBindingGraph);
156   }
157 
158   @Override
clearCache()159   public void clearCache() {
160     topLevelComponentCache.clear();
161   }
162 }
163