• 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.common.collect.Sets.union;
20 import static dagger.internal.codegen.ComponentAnnotation.allComponentAnnotations;
21 import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotations;
22 import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotations;
23 import static dagger.internal.codegen.ComponentCreatorAnnotation.allCreatorAnnotations;
24 import static dagger.internal.codegen.ComponentCreatorAnnotation.rootComponentCreatorAnnotations;
25 import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations;
26 import static dagger.internal.codegen.ValidationType.NONE;
27 import static java.util.Collections.disjoint;
28 
29 import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
30 import com.google.auto.common.MoreElements;
31 import com.google.common.base.Predicates;
32 import com.google.common.collect.ImmutableMap;
33 import com.google.common.collect.ImmutableSet;
34 import com.google.common.collect.Multimaps;
35 import com.google.common.collect.SetMultimap;
36 import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
37 import java.lang.annotation.Annotation;
38 import java.util.HashMap;
39 import java.util.Map;
40 import java.util.Set;
41 import javax.annotation.processing.Messager;
42 import javax.inject.Inject;
43 import javax.lang.model.element.Element;
44 import javax.lang.model.element.TypeElement;
45 
46 /**
47  * A {@link ProcessingStep} that is responsible for dealing with a component or production component
48  * as part of the {@link ComponentProcessor}.
49  */
50 final class ComponentProcessingStep extends TypeCheckingProcessingStep<TypeElement> {
51   private final Messager messager;
52   private final ComponentValidator componentValidator;
53   private final ComponentCreatorValidator creatorValidator;
54   private final ComponentDescriptorValidator componentDescriptorValidator;
55   private final ComponentDescriptorFactory componentDescriptorFactory;
56   private final BindingGraphFactory bindingGraphFactory;
57   private final SourceFileGenerator<BindingGraph> componentGenerator;
58   private final BindingGraphConverter bindingGraphConverter;
59   private final BindingGraphValidator bindingGraphValidator;
60   private final CompilerOptions compilerOptions;
61   private ImmutableSet<Element> subcomponentElements;
62   private ImmutableSet<Element> subcomponentCreatorElements;
63   private ImmutableMap<Element, ValidationReport<TypeElement>> creatorReportsByComponent;
64   private ImmutableMap<Element, ValidationReport<TypeElement>> creatorReportsBySubcomponent;
65   private ImmutableMap<Element, ValidationReport<TypeElement>> reportsBySubcomponent;
66 
67   @Inject
ComponentProcessingStep( Messager messager, ComponentValidator componentValidator, ComponentCreatorValidator creatorValidator, ComponentDescriptorValidator componentDescriptorValidator, ComponentDescriptorFactory componentDescriptorFactory, BindingGraphFactory bindingGraphFactory, SourceFileGenerator<BindingGraph> componentGenerator, BindingGraphConverter bindingGraphConverter, BindingGraphValidator bindingGraphValidator, CompilerOptions compilerOptions)68   ComponentProcessingStep(
69       Messager messager,
70       ComponentValidator componentValidator,
71       ComponentCreatorValidator creatorValidator,
72       ComponentDescriptorValidator componentDescriptorValidator,
73       ComponentDescriptorFactory componentDescriptorFactory,
74       BindingGraphFactory bindingGraphFactory,
75       SourceFileGenerator<BindingGraph> componentGenerator,
76       BindingGraphConverter bindingGraphConverter,
77       BindingGraphValidator bindingGraphValidator,
78       CompilerOptions compilerOptions) {
79     super(MoreElements::asType);
80     this.messager = messager;
81     this.componentValidator = componentValidator;
82     this.creatorValidator = creatorValidator;
83     this.componentDescriptorValidator = componentDescriptorValidator;
84     this.componentDescriptorFactory = componentDescriptorFactory;
85     this.bindingGraphFactory = bindingGraphFactory;
86     this.componentGenerator = componentGenerator;
87     this.bindingGraphConverter = bindingGraphConverter;
88     this.bindingGraphValidator = bindingGraphValidator;
89     this.compilerOptions = compilerOptions;
90   }
91 
92   @Override
annotations()93   public Set<Class<? extends Annotation>> annotations() {
94     return union(allComponentAnnotations(), allCreatorAnnotations());
95   }
96 
97   @Override
process( SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation)98   public ImmutableSet<Element> process(
99       SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
100     subcomponentElements =
101         getElementsFromAnnotations(elementsByAnnotation, subcomponentAnnotations());
102     subcomponentCreatorElements =
103         getElementsFromAnnotations(elementsByAnnotation, subcomponentCreatorAnnotations());
104 
105     ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
106 
107     creatorReportsByComponent =
108         processCreators(
109             getElementsFromAnnotations(elementsByAnnotation, rootComponentCreatorAnnotations()),
110             rejectedElements);
111     creatorReportsBySubcomponent = processCreators(subcomponentCreatorElements, rejectedElements);
112     reportsBySubcomponent =
113         processSubcomponents(subcomponentElements, subcomponentCreatorElements, rejectedElements);
114 
115     return rejectedElements.addAll(super.process(elementsByAnnotation)).build();
116   }
117 
118   @Override
process( TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations)119   protected void process(
120       TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
121     if (!disjoint(annotations, rootComponentAnnotations())) {
122       processRootComponent(element);
123     }
124     if (!disjoint(annotations, subcomponentAnnotations())) {
125       processSubcomponent(element);
126     }
127   }
128 
processRootComponent(TypeElement component)129   private void processRootComponent(TypeElement component) {
130     if (!isRootComponentValid(component)) {
131       return;
132     }
133     ComponentDescriptor componentDescriptor =
134         componentDescriptorFactory.rootComponentDescriptor(component);
135     if (!isValid(componentDescriptor)) {
136       return;
137     }
138     if (!isFullBindingGraphValid(componentDescriptor)) {
139       return;
140     }
141     BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor, false);
142     if (isValid(bindingGraph)) {
143       generateComponent(bindingGraph);
144     }
145   }
146 
processSubcomponent(TypeElement subcomponent)147   private void processSubcomponent(TypeElement subcomponent) {
148     if (!compilerOptions.aheadOfTimeSubcomponents()
149         && compilerOptions.fullBindingGraphValidationType(subcomponent).equals(NONE)) {
150       return;
151     }
152     if (!isSubcomponentValid(subcomponent)) {
153       return;
154     }
155     ComponentDescriptor subcomponentDescriptor =
156         componentDescriptorFactory.subcomponentDescriptor(subcomponent);
157     // TODO(dpb): ComponentDescriptorValidator for subcomponents, as we do for root components.
158     if (!isFullBindingGraphValid(subcomponentDescriptor)) {
159       return;
160     }
161     if (compilerOptions.aheadOfTimeSubcomponents()) {
162       BindingGraph bindingGraph = bindingGraphFactory.create(subcomponentDescriptor, false);
163       if (isValid(bindingGraph)) {
164         generateComponent(bindingGraph);
165       }
166     }
167   }
168 
generateComponent(BindingGraph bindingGraph)169   private void generateComponent(BindingGraph bindingGraph) {
170     componentGenerator.generate(bindingGraph, messager);
171   }
172 
getElementsFromAnnotations( final SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation, Set<Class<? extends Annotation>> annotations)173   static ImmutableSet<Element> getElementsFromAnnotations(
174       final SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation,
175       Set<Class<? extends Annotation>> annotations) {
176     return ImmutableSet.copyOf(
177         Multimaps.filterKeys(elementsByAnnotation, Predicates.in(annotations)).values());
178   }
179 
processCreators( Set<? extends Element> builderElements, ImmutableSet.Builder<Element> rejectedElements)180   private ImmutableMap<Element, ValidationReport<TypeElement>> processCreators(
181       Set<? extends Element> builderElements, ImmutableSet.Builder<Element> rejectedElements) {
182     // Can't use an ImmutableMap.Builder here because a component may have (invalidly) more than one
183     // builder type, and that would make ImmutableMap.Builder throw.
184     Map<Element, ValidationReport<TypeElement>> reports = new HashMap<>();
185     for (Element element : builderElements) {
186       try {
187         ValidationReport<TypeElement> report =
188             creatorValidator.validate(MoreElements.asType(element));
189         report.printMessagesTo(messager);
190         reports.put(element.getEnclosingElement(), report);
191       } catch (TypeNotPresentException e) {
192         rejectedElements.add(element);
193       }
194     }
195     return ImmutableMap.copyOf(reports);
196   }
197 
processSubcomponents( Set<? extends Element> subcomponentElements, Set<? extends Element> subcomponentBuilderElements, ImmutableSet.Builder<Element> rejectedElements)198   private ImmutableMap<Element, ValidationReport<TypeElement>> processSubcomponents(
199       Set<? extends Element> subcomponentElements,
200       Set<? extends Element> subcomponentBuilderElements,
201       ImmutableSet.Builder<Element> rejectedElements) {
202     ImmutableMap.Builder<Element, ValidationReport<TypeElement>> reports = ImmutableMap.builder();
203     for (Element element : subcomponentElements) {
204       try {
205         ComponentValidationReport report =
206             componentValidator.validate(
207                 MoreElements.asType(element), subcomponentElements, subcomponentBuilderElements);
208         report.report().printMessagesTo(messager);
209         reports.put(element, report.report());
210       } catch (TypeNotPresentException e) {
211         rejectedElements.add(element);
212       }
213     }
214     return reports.build();
215   }
216 
isRootComponentValid(TypeElement rootComponent)217   private boolean isRootComponentValid(TypeElement rootComponent) {
218     ComponentValidationReport validationReport =
219         componentValidator.validate(
220             rootComponent, subcomponentElements, subcomponentCreatorElements);
221     validationReport.report().printMessagesTo(messager);
222     return isClean(validationReport);
223   }
224 
225   // TODO(dpb): Clean up generics so this can take TypeElement.
isSubcomponentValid(Element subcomponentElement)226   private boolean isSubcomponentValid(Element subcomponentElement) {
227     ValidationReport<?> subcomponentCreatorReport =
228         creatorReportsBySubcomponent.get(subcomponentElement);
229     if (subcomponentCreatorReport != null && !subcomponentCreatorReport.isClean()) {
230       return false;
231     }
232     ValidationReport<?> subcomponentReport = reportsBySubcomponent.get(subcomponentElement);
233     return subcomponentReport == null || subcomponentReport.isClean();
234   }
235 
isFullBindingGraphValid(ComponentDescriptor componentDescriptor)236   private boolean isFullBindingGraphValid(ComponentDescriptor componentDescriptor) {
237     if (compilerOptions
238         .fullBindingGraphValidationType(componentDescriptor.typeElement())
239         .equals(NONE)) {
240       return true;
241     }
242     BindingGraph fullBindingGraph = bindingGraphFactory.create(componentDescriptor, true);
243     return isValid(fullBindingGraph);
244   }
245 
isValid(ComponentDescriptor componentDescriptor)246   private boolean isValid(ComponentDescriptor componentDescriptor) {
247     ValidationReport<TypeElement> componentDescriptorReport =
248         componentDescriptorValidator.validate(componentDescriptor);
249     componentDescriptorReport.printMessagesTo(messager);
250     return componentDescriptorReport.isClean();
251   }
252 
isValid(BindingGraph bindingGraph)253   private boolean isValid(BindingGraph bindingGraph) {
254     return bindingGraphValidator.isValid(bindingGraphConverter.convert(bindingGraph));
255   }
256 
257   /**
258    * Returns true if the component's report is clean, its builder report is clean, and all
259    * referenced subcomponent reports and subcomponent builder reports are clean.
260    */
isClean(ComponentValidationReport report)261   private boolean isClean(ComponentValidationReport report) {
262     Element component = report.report().subject();
263     ValidationReport<?> componentReport = report.report();
264     if (!componentReport.isClean()) {
265       return false;
266     }
267     ValidationReport<?> builderReport = creatorReportsByComponent.get(component);
268     if (builderReport != null && !builderReport.isClean()) {
269       return false;
270     }
271     for (Element element : report.referencedSubcomponents()) {
272       if (!isSubcomponentValid(element)) {
273         return false;
274       }
275     }
276     return true;
277   }
278 }
279