/*
* Copyright (C) 2014 Google, Inc.
*
* 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 com.google.auto.common.BasicAnnotationProcessor.ProcessingStep;
import com.google.auto.common.MoreElements;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
import dagger.Component;
import dagger.Subcomponent;
import dagger.internal.codegen.ComponentDescriptor.Factory;
import dagger.internal.codegen.ComponentValidator.ComponentValidationReport;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
/**
* A {@link ProcessingStep} that is responsible for dealing with the {@link Component} annotation
* as part of the {@link ComponentProcessor}.
*
* @author Gregory Kick
*/
final class ComponentProcessingStep extends AbstractComponentProcessingStep {
private final Messager messager;
private final ComponentValidator componentValidator;
private final ComponentValidator subcomponentValidator;
private final BuilderValidator componentBuilderValidator;
private final BuilderValidator subcomponentBuilderValidator;
ComponentProcessingStep(
Messager messager,
ComponentValidator componentValidator,
ComponentValidator subcomponentValidator,
BuilderValidator componentBuilderValidator,
BuilderValidator subcomponentBuilderValidator,
ComponentHierarchyValidator componentHierarchyValidator,
BindingGraphValidator bindingGraphValidator,
Factory componentDescriptorFactory,
BindingGraph.Factory bindingGraphFactory,
ComponentGenerator componentGenerator) {
super(
Component.class,
messager,
componentHierarchyValidator,
bindingGraphValidator,
componentDescriptorFactory,
bindingGraphFactory,
componentGenerator);
this.messager = messager;
this.componentValidator = componentValidator;
this.subcomponentValidator = subcomponentValidator;
this.componentBuilderValidator = componentBuilderValidator;
this.subcomponentBuilderValidator = subcomponentBuilderValidator;
}
@Override
public Set<Class<? extends Annotation>> annotations() {
return ImmutableSet.<Class<? extends Annotation>>of(Component.class, Component.Builder.class,
Subcomponent.class, Subcomponent.Builder.class);
}
@Override
protected ComponentElementValidator componentElementValidator(
SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
final Map<Element, ValidationReport<TypeElement>> builderReportsByComponent =
processComponentBuilders(elementsByAnnotation.get(Component.Builder.class));
final Set<Element> subcomponentBuilderElements =
elementsByAnnotation.get(Subcomponent.Builder.class);
final Map<Element, ValidationReport<TypeElement>> builderReportsBySubcomponent =
processSubcomponentBuilders(subcomponentBuilderElements);
final Set<Element> subcomponentElements = elementsByAnnotation.get(Subcomponent.class);
final Map<Element, ValidationReport<TypeElement>> reportsBySubcomponent =
processSubcomponents(subcomponentElements, subcomponentBuilderElements);
return new ComponentElementValidator() {
@Override
boolean validateComponent(TypeElement componentTypeElement, Messager messager) {
ComponentValidationReport validationReport =
componentValidator.validate(
componentTypeElement, subcomponentElements, subcomponentBuilderElements);
validationReport.report().printMessagesTo(messager);
return isClean(
validationReport,
builderReportsByComponent,
reportsBySubcomponent,
builderReportsBySubcomponent);
}
};
}
private Map<Element, ValidationReport<TypeElement>> processComponentBuilders(
Set<? extends Element> componentBuilderElements) {
Map<Element, ValidationReport<TypeElement>> builderReportsByComponent = Maps.newHashMap();
for (Element element : componentBuilderElements) {
ValidationReport<TypeElement> report =
componentBuilderValidator.validate(MoreElements.asType(element));
report.printMessagesTo(messager);
builderReportsByComponent.put(element.getEnclosingElement(), report);
}
return builderReportsByComponent;
}
private Map<Element, ValidationReport<TypeElement>> processSubcomponentBuilders(
Set<? extends Element> subcomponentBuilderElements) {
Map<Element, ValidationReport<TypeElement>> builderReportsBySubcomponent = Maps.newHashMap();
for (Element element : subcomponentBuilderElements) {
ValidationReport<TypeElement> report =
subcomponentBuilderValidator.validate(MoreElements.asType(element));
report.printMessagesTo(messager);
builderReportsBySubcomponent.put(element, report);
}
return builderReportsBySubcomponent;
}
private Map<Element, ValidationReport<TypeElement>> processSubcomponents(
Set<? extends Element> subcomponentElements,
Set<? extends Element> subcomponentBuilderElements) {
Map<Element, ValidationReport<TypeElement>> reportsBySubcomponent = Maps.newHashMap();
for (Element element : subcomponentElements) {
ComponentValidationReport report = subcomponentValidator.validate(
MoreElements.asType(element), subcomponentElements, subcomponentBuilderElements);
report.report().printMessagesTo(messager);
reportsBySubcomponent.put(element, report.report());
}
return reportsBySubcomponent;
}
/**
* Returns true if the component's report is clean, its builder report is clean, and all
* referenced subcomponent reports & subcomponent builder reports are clean.
*/
private boolean isClean(ComponentValidationReport report,
Map<Element, ValidationReport<TypeElement>> builderReportsByComponent,
Map<Element, ValidationReport<TypeElement>> reportsBySubcomponent,
Map<Element, ValidationReport<TypeElement>> builderReportsBySubcomponent) {
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()) {
ValidationReport<?> subcomponentBuilderReport = builderReportsBySubcomponent.get(element);
if (subcomponentBuilderReport != null && !subcomponentBuilderReport.isClean()) {
return false;
}
ValidationReport<?> subcomponentReport = reportsBySubcomponent.get(element);
if (subcomponentReport != null && !subcomponentReport.isClean()) {
return false;
}
}
return true;
}
}