blob: 3902743d88143edff0420f85d95f0708722ff829 [file] [log] [blame]
/*
* Copyright (C) 2018 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.auto.common.MoreElements.asType;
import static com.google.auto.common.MoreTypes.asTypeElement;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.concat;
import static dagger.internal.codegen.DaggerStreams.instancesOf;
import static dagger.model.BindingGraphProxies.bindingNode;
import static dagger.model.BindingGraphProxies.childFactoryMethodEdge;
import static dagger.model.BindingGraphProxies.componentNode;
import static dagger.model.BindingGraphProxies.dependencyEdge;
import static dagger.model.BindingGraphProxies.subcomponentBuilderBindingEdge;
import static dagger.model.BindingKind.SUBCOMPONENT_BUILDER;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.graph.MutableNetwork;
import com.google.common.graph.NetworkBuilder;
import dagger.internal.codegen.ComponentDescriptor.ComponentMethodDescriptor;
import dagger.model.BindingGraph.BindingNode;
import dagger.model.BindingGraph.ComponentNode;
import dagger.model.BindingGraph.DependencyEdge;
import dagger.model.BindingGraph.Edge;
import dagger.model.BindingGraph.Node;
import dagger.model.BindingGraphProxies;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
/** Converts {@link dagger.internal.codegen.BindingGraph}s to {@link dagger.model.BindingGraph}s. */
final class BindingGraphConverter extends ComponentTreeTraverser {
private final MutableNetwork<Node, Edge> network =
NetworkBuilder.directed().allowsParallelEdges(true).allowsSelfLoops(true).build();
private ComponentNode parentComponent;
private ComponentNode currentComponent;
private BindingGraphConverter(BindingGraph graph) {
super(graph);
}
/**
* Creates the external {@link dagger.model.BindingGraph} representing the given internal root
* {@link dagger.internal.codegen.BindingGraph}.
*/
static dagger.model.BindingGraph convert(BindingGraph graph) {
BindingGraphConverter converter = new BindingGraphConverter(graph);
converter.traverseComponents();
return BindingGraphProxies.bindingGraph(converter.network);
}
@Override
protected void visitComponent(BindingGraph graph) {
ComponentNode grandparentNode = parentComponent;
parentComponent = currentComponent;
currentComponent = componentNode(componentTreePath().toComponentPath());
network.addNode(currentComponent);
super.visitComponent(graph);
currentComponent = parentComponent;
parentComponent = grandparentNode;
}
@Override
protected void visitSubcomponentFactoryMethod(
BindingGraph graph, BindingGraph parent, ExecutableElement factoryMethod) {
network.addEdge(parentComponent, currentComponent, childFactoryMethodEdge(factoryMethod));
super.visitSubcomponentFactoryMethod(graph, parent, factoryMethod);
}
@Override
protected BindingGraphTraverser bindingGraphTraverser(
ComponentTreePath componentTreePath, ComponentMethodDescriptor entryPointMethod) {
return new BindingGraphVisitor(componentTreePath, entryPointMethod);
}
private final class BindingGraphVisitor extends BindingGraphTraverser {
private Node current;
BindingGraphVisitor(
ComponentTreePath componentTreePath, ComponentMethodDescriptor entryPointMethod) {
super(componentTreePath, entryPointMethod);
current = currentComponent;
network.addNode(current);
}
@Override
protected void visitBinding(Binding binding, ComponentDescriptor owningComponent) {
// TODO(dpb): Should we visit only bindings owned by the current component, since other
// bindings will be visited in the parent?
Node previous = current;
current = newBindingNode(resolvedBindings(), binding, owningComponent);
network.addNode(current);
if (binding instanceof ContributionBinding) {
ContributionBinding contributionBinding = (ContributionBinding) binding;
if (contributionBinding.kind().equals(SUBCOMPONENT_BUILDER)) {
ImmutableSet.Builder<TypeElement> modules = ImmutableSet.builder();
for (SubcomponentDeclaration subcomponentDeclaration :
resolvedBindings().subcomponentDeclarations()) {
modules.add(subcomponentDeclaration.contributingModule().get());
}
network.addEdge(
current,
subcomponentNode(contributionBinding, owningComponent),
subcomponentBuilderBindingEdge(modules.build()));
}
}
if (network
.edgesConnecting(previous, current)
.stream()
.flatMap(instancesOf(DependencyEdge.class))
.noneMatch(e -> e.dependencyRequest().equals(dependencyRequest()))) {
network.addEdge(
previous, current, dependencyEdge(dependencyRequest(), atEntryPoint()));
super.visitBinding(binding, owningComponent);
}
current = previous;
}
private ComponentNode subcomponentNode(
ContributionBinding binding, ComponentDescriptor subcomponentParent) {
checkArgument(binding.kind().equals(SUBCOMPONENT_BUILDER));
TypeElement builderType = asTypeElement(binding.key().type());
TypeElement subcomponentType = asType(builderType.getEnclosingElement());
ComponentTreePath childPath =
componentTreePath()
.pathFromRootToAncestor(subcomponentParent)
.childPath(subcomponentType);
ComponentNode childNode = componentNode(childPath.toComponentPath());
network.addNode(childNode);
return childNode;
}
private BindingNode newBindingNode(
ResolvedBindings resolvedBindings, Binding binding, ComponentDescriptor owningComponent) {
ImmutableList.Builder<Element> associatedDeclarations = ImmutableList.builder();
for (BindingDeclaration declaration :
concat(
resolvedBindings.multibindingDeclarations(),
resolvedBindings.optionalBindingDeclarations(),
resolvedBindings.subcomponentDeclarations())) {
associatedDeclarations.add(declaration.bindingElement().get());
}
return bindingNode(
componentTreePath().pathFromRootToAncestor(owningComponent).toComponentPath(),
binding,
associatedDeclarations.build());
}
}
}