/* * Copyright (C) 2016 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.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; import static dagger.internal.codegen.DaggerStreams.toImmutableList; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor; import dagger.model.ComponentPath; import dagger.model.DependencyRequest; import java.util.ArrayDeque; import java.util.Deque; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; /** * An object that traverses the entire component hierarchy, starting from the root component. * *
Subclasses can override {@link #visitComponent(BindingGraph)} to perform custom logic at each
* component in the tree, and {@link #visitSubcomponentFactoryMethod(BindingGraph, BindingGraph,
* ExecutableElement)} to perform custom logic at each subcomponent factory method.
*/
public class ComponentTreeTraverser {
/** The path from the root graph to the currently visited graph. */
private final Deque Subclasses can override this method to perform whatever logic is required per component.
* They should call the {@code super} implementation if they want to continue the traversal in the
* standard order.
*
* This implementation does the following:
*
* This implementation does nothing.
*
* @param graph the currently visited graph
* @param parent the parent graph
* @param factoryMethod the factory method in the parent component that declares that the current
* component is a child
*/
protected void visitSubcomponentFactoryMethod(
BindingGraph graph, BindingGraph parent, ExecutableElement factoryMethod) {}
/**
* Called once for each entry point in a component.
*
* Subclasses can override this method to perform whatever logic is required per entry point.
* They should call the {@code super} implementation if they want to continue the traversal in the
* standard order.
*
* This implementation does nothing.
*
* @param graph the graph for the component that contains the entry point
*/
protected void visitEntryPoint(DependencyRequest entryPoint, BindingGraph graph) {}
/**
* Returns an immutable snapshot of the path from the root component to the currently visited
* component.
*/
protected final ComponentPath componentPath() {
return componentPaths.getLast();
}
/**
* Returns the subpath from the root component to the matching {@code ancestor} of the current
* component.
*/
protected final ComponentPath pathFromRootToAncestor(TypeElement ancestor) {
for (ComponentPath componentPath : componentPaths) {
if (componentPath.currentComponent().equals(ancestor)) {
return componentPath;
}
}
throw new IllegalArgumentException(
String.format("%s is not in the current path: %s", ancestor.getQualifiedName(), this));
}
/**
* Returns the BindingGraph for {@code ancestor}, where {@code ancestor} is in the component path
* of the current traversal.
*/
protected final BindingGraph graphForAncestor(TypeElement ancestor) {
for (BindingGraph graph : bindingGraphPath) {
if (graph.componentTypeElement().equals(ancestor)) {
return graph;
}
}
throw new IllegalArgumentException(
String.format("%s is not in the current path: %s", ancestor.getQualifiedName(), this));
}
}
*
*
* @param graph the currently visited graph
*/
protected void visitComponent(BindingGraph graph) {
if (bindingGraphPath.size() > 1) {
BindingGraph parent = Iterators.get(bindingGraphPath.descendingIterator(), 1);
parent
.componentDescriptor()
.getFactoryMethodForChildComponent(graph.componentDescriptor())
.ifPresent(
childFactoryMethod ->
visitSubcomponentFactoryMethod(
graph, parent, childFactoryMethod.methodElement()));
}
for (ComponentMethodDescriptor entryPointMethod :
graph.componentDescriptor().entryPointMethods()) {
visitEntryPoint(entryPointMethod.dependencyRequest().get(), graph);
}
for (BindingGraph child : graph.subgraphs()) {
bindingGraphPath.addLast(child);
ComponentPath childPath =
ComponentPath.create(
bindingGraphPath.stream()
.map(BindingGraph::componentTypeElement)
.collect(toImmutableList()));
componentPaths.addLast(childPath);
try {
visitComponent(child);
} finally {
verify(bindingGraphPath.removeLast().equals(child));
verify(componentPaths.removeLast().equals(childPath));
}
}
}
/**
* Called if this component was installed in its parent by a subcomponent factory method.
*
*