blob: ad25a61666b057b3c96accee6196bfe88791f30f [file] [log] [blame]
/*
* 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 dagger.internal.codegen.ComponentKind.allComponentAndBuilderAnnotations;
import static dagger.internal.codegen.ComponentKind.annotationsFor;
import static dagger.internal.codegen.ComponentKind.builderAnnotationsFor;
import static dagger.internal.codegen.ComponentKind.rootComponentKinds;
import static dagger.internal.codegen.ComponentKind.subcomponentKinds;
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<TypeElement> {
private final Messager messager;
private final ComponentValidator componentValidator;
private final ComponentCreatorValidator creatorValidator;
private final ComponentDescriptorValidator componentDescriptorValidator;
private final ComponentDescriptor.Factory componentDescriptorFactory;
private final BindingGraphFactory bindingGraphFactory;
private final SourceFileGenerator<BindingGraph> componentGenerator;
private final BindingGraphConverter bindingGraphConverter;
private final BindingGraphValidator bindingGraphValidator;
private final CompilerOptions compilerOptions;
private ImmutableSet<Element> subcomponentElements;
private ImmutableSet<Element> subcomponentBuilderElements;
private ImmutableMap<Element, ValidationReport<TypeElement>> builderReportsByComponent;
private ImmutableMap<Element, ValidationReport<TypeElement>> builderReportsBySubcomponent;
private ImmutableMap<Element, ValidationReport<TypeElement>> reportsBySubcomponent;
@Inject
ComponentProcessingStep(
Messager messager,
ComponentValidator componentValidator,
ComponentCreatorValidator creatorValidator,
ComponentDescriptorValidator componentDescriptorValidator,
ComponentDescriptor.Factory componentDescriptorFactory,
BindingGraphFactory bindingGraphFactory,
SourceFileGenerator<BindingGraph> 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<Class<? extends Annotation>> annotations() {
return allComponentAndBuilderAnnotations();
}
@Override
public ImmutableSet<Element> process(
SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
subcomponentElements =
getElementsFromAnnotations(elementsByAnnotation, annotationsFor(subcomponentKinds()));
subcomponentBuilderElements =
getElementsFromAnnotations(
elementsByAnnotation, builderAnnotationsFor(subcomponentKinds()));
ImmutableSet.Builder<Element> rejectedElements = ImmutableSet.builder();
builderReportsByComponent =
processBuilders(
getElementsFromAnnotations(
elementsByAnnotation, builderAnnotationsFor(rootComponentKinds())),
rejectedElements);
builderReportsBySubcomponent = processBuilders(subcomponentBuilderElements, rejectedElements);
reportsBySubcomponent =
processSubcomponents(subcomponentElements, subcomponentBuilderElements, rejectedElements);
return rejectedElements.addAll(super.process(elementsByAnnotation)).build();
}
@Override
protected void process(
TypeElement element, ImmutableSet<Class<? extends Annotation>> annotations) {
if (!disjoint(annotations, annotationsFor(rootComponentKinds()))) {
ComponentValidationReport validationReport =
componentValidator.validate(element, subcomponentElements, subcomponentBuilderElements);
validationReport.report().printMessagesTo(messager);
if (!isClean(validationReport)) {
return;
}
ComponentDescriptor componentDescriptor = componentDescriptorFactory.forTypeElement(element);
ValidationReport<TypeElement> componentDescriptorReport =
componentDescriptorValidator.validate(componentDescriptor);
componentDescriptorReport.printMessagesTo(messager);
if (!componentDescriptorReport.isClean()) {
return;
}
BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor);
if (isValid(bindingGraph)) {
generateComponent(bindingGraph);
}
}
if (compilerOptions.aheadOfTimeSubcomponents()
&& !disjoint(annotations, annotationsFor(subcomponentKinds()))) {
if (!subcomponentIsClean(element)) {
return;
}
ComponentDescriptor componentDescriptor = componentDescriptorFactory.forTypeElement(element);
BindingGraph bindingGraph = bindingGraphFactory.create(componentDescriptor);
if (isValid(bindingGraph)) {
generateComponent(bindingGraph);
}
}
}
private boolean isValid(BindingGraph bindingGraph) {
dagger.model.BindingGraph modelGraph = bindingGraphConverter.convert(bindingGraph);
return bindingGraphValidator.isValid(modelGraph);
}
private void generateComponent(BindingGraph bindingGraph) {
componentGenerator.generate(bindingGraph, messager);
}
static ImmutableSet<Element> getElementsFromAnnotations(
final SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation,
Set<Class<? extends Annotation>> annotations) {
return ImmutableSet.copyOf(
Multimaps.filterKeys(elementsByAnnotation, Predicates.in(annotations)).values());
}
private ImmutableMap<Element, ValidationReport<TypeElement>> processBuilders(
Set<? extends Element> builderElements, ImmutableSet.Builder<Element> 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<Element, ValidationReport<TypeElement>> reports = new HashMap<>();
for (Element element : builderElements) {
try {
ValidationReport<TypeElement> 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<Element, ValidationReport<TypeElement>> processSubcomponents(
Set<? extends Element> subcomponentElements,
Set<? extends Element> subcomponentBuilderElements,
ImmutableSet.Builder<Element> rejectedElements) {
ImmutableMap.Builder<Element, ValidationReport<TypeElement>> 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();
}
/**
* 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 = builderReportsByComponent.get(component);
if (builderReport != null && !builderReport.isClean()) {
return false;
}
for (Element element : report.referencedSubcomponents()) {
if (!subcomponentIsClean(element)) {
return false;
}
}
return true;
}
/** Returns true if the reports associated with the subcomponent are clean. */
private boolean subcomponentIsClean(Element subcomponentElement) {
ValidationReport<?> subcomponentBuilderReport =
builderReportsBySubcomponent.get(subcomponentElement);
if (subcomponentBuilderReport != null && !subcomponentBuilderReport.isClean()) {
return false;
}
ValidationReport<?> subcomponentReport = reportsBySubcomponent.get(subcomponentElement);
if (subcomponentReport != null && !subcomponentReport.isClean()) {
return false;
}
return true;
}
}