/* * Copyright (C) 2014 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen; import static com.google.common.collect.Sets.union; import static dagger.internal.codegen.ComponentAnnotation.allComponentAnnotations; import static dagger.internal.codegen.ComponentAnnotation.rootComponentAnnotations; import static dagger.internal.codegen.ComponentAnnotation.subcomponentAnnotations; import static dagger.internal.codegen.ComponentCreatorAnnotation.allCreatorAnnotations; import static dagger.internal.codegen.ComponentCreatorAnnotation.rootComponentCreatorAnnotations; import static dagger.internal.codegen.ComponentCreatorAnnotation.subcomponentCreatorAnnotations; import static dagger.internal.codegen.ValidationType.NONE; import static java.util.Collections.disjoint; import com.google.auto.common.BasicAnnotationProcessor.ProcessingStep; import com.google.auto.common.MoreElements; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; import dagger.internal.codegen.ComponentValidator.ComponentValidationReport; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.annotation.processing.Messager; import javax.inject.Inject; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; /** * A {@link ProcessingStep} that is responsible for dealing with a component or production component * as part of the {@link ComponentProcessor}. */ final class ComponentProcessingStep extends TypeCheckingProcessingStep { private final Messager messager; private final ComponentValidator componentValidator; private final ComponentCreatorValidator creatorValidator; private final ComponentDescriptorValidator componentDescriptorValidator; private final ComponentDescriptorFactory componentDescriptorFactory; private final BindingGraphFactory bindingGraphFactory; private final SourceFileGenerator componentGenerator; private final BindingGraphConverter bindingGraphConverter; private final BindingGraphValidator bindingGraphValidator; private final CompilerOptions compilerOptions; private ImmutableSet subcomponentElements; private ImmutableSet subcomponentCreatorElements; private ImmutableMap> creatorReportsByComponent; private ImmutableMap> creatorReportsBySubcomponent; private ImmutableMap> reportsBySubcomponent; @Inject ComponentProcessingStep( Messager messager, ComponentValidator componentValidator, ComponentCreatorValidator creatorValidator, ComponentDescriptorValidator componentDescriptorValidator, ComponentDescriptorFactory componentDescriptorFactory, BindingGraphFactory bindingGraphFactory, SourceFileGenerator componentGenerator, BindingGraphConverter bindingGraphConverter, BindingGraphValidator bindingGraphValidator, CompilerOptions compilerOptions) { super(MoreElements::asType); this.messager = messager; this.componentValidator = componentValidator; this.creatorValidator = creatorValidator; this.componentDescriptorValidator = componentDescriptorValidator; this.componentDescriptorFactory = componentDescriptorFactory; this.bindingGraphFactory = bindingGraphFactory; this.componentGenerator = componentGenerator; this.bindingGraphConverter = bindingGraphConverter; this.bindingGraphValidator = bindingGraphValidator; this.compilerOptions = compilerOptions; } @Override public Set> annotations() { return union(allComponentAnnotations(), allCreatorAnnotations()); } @Override public ImmutableSet process( SetMultimap, Element> elementsByAnnotation) { subcomponentElements = getElementsFromAnnotations(elementsByAnnotation, subcomponentAnnotations()); subcomponentCreatorElements = getElementsFromAnnotations(elementsByAnnotation, subcomponentCreatorAnnotations()); ImmutableSet.Builder rejectedElements = ImmutableSet.builder(); creatorReportsByComponent = processCreators( getElementsFromAnnotations(elementsByAnnotation, rootComponentCreatorAnnotations()), rejectedElements); creatorReportsBySubcomponent = processCreators(subcomponentCreatorElements, rejectedElements); reportsBySubcomponent = processSubcomponents(subcomponentElements, subcomponentCreatorElements, rejectedElements); return rejectedElements.addAll(super.process(elementsByAnnotation)).build(); } @Override protected void process( TypeElement element, ImmutableSet> annotations) { if (!disjoint(annotations, rootComponentAnnotations())) { processRootComponent(element); } if (!disjoint(annotations, subcomponentAnnotations())) { processSubcomponent(element); } } private void processRootComponent(TypeElement component) { if (!isRootComponentValid(component)) { return; } ComponentDescriptor componentDescriptor = componentDescriptorFactory.rootComponentDescriptor(component); if (!isValid(componentDescriptor)) { return; } if (!isFullBindingGraphValid(componentDescriptor)) { return; } BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor, false); if (isValid(bindingGraph)) { generateComponent(bindingGraph); } } private void processSubcomponent(TypeElement subcomponent) { if (!compilerOptions.aheadOfTimeSubcomponents() && compilerOptions.fullBindingGraphValidationType(subcomponent).equals(NONE)) { return; } if (!isSubcomponentValid(subcomponent)) { return; } ComponentDescriptor subcomponentDescriptor = componentDescriptorFactory.subcomponentDescriptor(subcomponent); // TODO(dpb): ComponentDescriptorValidator for subcomponents, as we do for root components. if (!isFullBindingGraphValid(subcomponentDescriptor)) { return; } if (compilerOptions.aheadOfTimeSubcomponents()) { BindingGraph bindingGraph = bindingGraphFactory.create(subcomponentDescriptor, false); if (isValid(bindingGraph)) { generateComponent(bindingGraph); } } } private void generateComponent(BindingGraph bindingGraph) { componentGenerator.generate(bindingGraph, messager); } static ImmutableSet getElementsFromAnnotations( final SetMultimap, Element> elementsByAnnotation, Set> annotations) { return ImmutableSet.copyOf( Multimaps.filterKeys(elementsByAnnotation, Predicates.in(annotations)).values()); } private ImmutableMap> processCreators( Set builderElements, ImmutableSet.Builder rejectedElements) { // Can't use an ImmutableMap.Builder here because a component may have (invalidly) more than one // builder type, and that would make ImmutableMap.Builder throw. Map> reports = new HashMap<>(); for (Element element : builderElements) { try { ValidationReport report = creatorValidator.validate(MoreElements.asType(element)); report.printMessagesTo(messager); reports.put(element.getEnclosingElement(), report); } catch (TypeNotPresentException e) { rejectedElements.add(element); } } return ImmutableMap.copyOf(reports); } private ImmutableMap> processSubcomponents( Set subcomponentElements, Set subcomponentBuilderElements, ImmutableSet.Builder rejectedElements) { ImmutableMap.Builder> reports = ImmutableMap.builder(); for (Element element : subcomponentElements) { try { ComponentValidationReport report = componentValidator.validate( MoreElements.asType(element), subcomponentElements, subcomponentBuilderElements); report.report().printMessagesTo(messager); reports.put(element, report.report()); } catch (TypeNotPresentException e) { rejectedElements.add(element); } } return reports.build(); } private boolean isRootComponentValid(TypeElement rootComponent) { ComponentValidationReport validationReport = componentValidator.validate( rootComponent, subcomponentElements, subcomponentCreatorElements); validationReport.report().printMessagesTo(messager); return isClean(validationReport); } // TODO(dpb): Clean up generics so this can take TypeElement. private boolean isSubcomponentValid(Element subcomponentElement) { ValidationReport subcomponentCreatorReport = creatorReportsBySubcomponent.get(subcomponentElement); if (subcomponentCreatorReport != null && !subcomponentCreatorReport.isClean()) { return false; } ValidationReport subcomponentReport = reportsBySubcomponent.get(subcomponentElement); return subcomponentReport == null || subcomponentReport.isClean(); } private boolean isFullBindingGraphValid(ComponentDescriptor componentDescriptor) { if (compilerOptions .fullBindingGraphValidationType(componentDescriptor.typeElement()) .equals(NONE)) { return true; } BindingGraph fullBindingGraph = bindingGraphFactory.create(componentDescriptor, true); return isValid(fullBindingGraph); } private boolean isValid(ComponentDescriptor componentDescriptor) { ValidationReport componentDescriptorReport = componentDescriptorValidator.validate(componentDescriptor); componentDescriptorReport.printMessagesTo(messager); return componentDescriptorReport.isClean(); } private boolean isValid(BindingGraph bindingGraph) { return bindingGraphValidator.isValid(bindingGraphConverter.convert(bindingGraph)); } /** * Returns true if the component's report is clean, its builder report is clean, and all * referenced subcomponent reports and subcomponent builder reports are clean. */ private boolean isClean(ComponentValidationReport report) { Element component = report.report().subject(); ValidationReport componentReport = report.report(); if (!componentReport.isClean()) { return false; } ValidationReport builderReport = creatorReportsByComponent.get(component); if (builderReport != null && !builderReport.isClean()) { return false; } for (Element element : report.referencedSubcomponents()) { if (!isSubcomponentValid(element)) { return false; } } return true; } }