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