|
|
/*
|
|
|
* 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.model;
|
|
|
|
|
|
import static com.google.common.collect.Sets.intersection;
|
|
|
import static com.google.common.graph.Graphs.inducedSubgraph;
|
|
|
import static com.google.common.graph.Graphs.reachableNodes;
|
|
|
import static com.google.common.graph.Graphs.transpose;
|
|
|
import static dagger.internal.codegen.extension.DaggerStreams.instancesOf;
|
|
|
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
|
|
|
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSetMultimap;
|
|
|
|
|
|
import com.google.common.collect.ImmutableSet;
|
|
|
import com.google.common.collect.ImmutableSetMultimap;
|
|
|
import com.google.common.graph.EndpointPair;
|
|
|
import com.google.common.graph.ImmutableNetwork;
|
|
|
import com.google.common.graph.MutableNetwork;
|
|
|
import com.google.common.graph.Network;
|
|
|
import com.google.common.graph.NetworkBuilder;
|
|
|
import dagger.Module;
|
|
|
import java.util.Optional;
|
|
|
import java.util.stream.Stream;
|
|
|
import javax.lang.model.element.ExecutableElement;
|
|
|
import javax.lang.model.element.TypeElement;
|
|
|
|
|
|
/**
|
|
|
* A graph of bindings, dependency requests, and components.
|
|
|
*
|
|
|
* <p>A {@link BindingGraph} represents one of the following:
|
|
|
*
|
|
|
* <ul>
|
|
|
* <li>an entire component hierarchy rooted at a {@link dagger.Component} or {@link
|
|
|
* dagger.producers.ProductionComponent}
|
|
|
* <li>a partial component hierarchy rooted at a {@link dagger.Subcomponent} or {@link
|
|
|
* dagger.producers.ProductionSubcomponent} (only when the value of {@code
|
|
|
* -Adagger.fullBindingGraphValidation} is not {@code NONE})
|
|
|
* <li>the bindings installed by a {@link Module} or {@link dagger.producers.ProducerModule},
|
|
|
* including all subcomponents generated by {@link Module#subcomponents()} ()} and {@link
|
|
|
* dagger.producers.ProducerModule#subcomponents()} ()}
|
|
|
* </ul>
|
|
|
*
|
|
|
* In the case of a {@link BindingGraph} representing a module, the root {@link ComponentNode} will
|
|
|
* actually represent the module type. The graph will also be a {@linkplain #isFullBindingGraph()
|
|
|
* full binding graph}, which means it will contain all bindings in all modules, as well as nodes
|
|
|
* for their dependencies. Otherwise it will contain only bindings that are reachable from at least
|
|
|
* one {@linkplain #entryPointEdges() entry point}.
|
|
|
*
|
|
|
* <h3>Nodes</h3>
|
|
|
*
|
|
|
* <p>There is a <b>{@link Binding}</b> for each owned binding in the graph. If a binding is owned
|
|
|
* by more than one component, there is one binding object for that binding for every owning
|
|
|
* component.
|
|
|
*
|
|
|
* <p>There is a <b>{@linkplain ComponentNode component node}</b> (without a binding) for each
|
|
|
* component in the graph.
|
|
|
*
|
|
|
* <h3>Edges</h3>
|
|
|
*
|
|
|
* <p>There is a <b>{@linkplain DependencyEdge dependency edge}</b> for each dependency request in
|
|
|
* the graph. Its target node is the binding for the binding that satisfies the request. For entry
|
|
|
* point dependency requests, the source node is the component node for the component for which it
|
|
|
* is an entry point. For other dependency requests, the source node is the binding for the binding
|
|
|
* that contains the request.
|
|
|
*
|
|
|
* <p>There is a <b>subcomponent edge</b> for each parent-child component relationship in the graph.
|
|
|
* The target node is the component node for the child component. For subcomponents defined by a
|
|
|
* {@linkplain SubcomponentCreatorBindingEdge subcomponent creator binding} (either a method on the
|
|
|
* component or a set of {@code @Module.subcomponents} annotation values), the source node is the
|
|
|
* binding for the {@code @Subcomponent.Builder} type. For subcomponents defined by {@linkplain
|
|
|
* ChildFactoryMethodEdge subcomponent factory methods}, the source node is the component node for
|
|
|
* the parent.
|
|
|
*
|
|
|
* <p><b>Note that this API is experimental and will change.</b>
|
|
|
*/
|
|
|
public abstract class BindingGraph {
|
|
|
/** Returns the graph in its {@link Network} representation. */
|
|
|
public abstract ImmutableNetwork<Node, Edge> network();
|
|
|
|
|
|
@Override
|
|
|
public String toString() {
|
|
|
return network().toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns {@code true} if this graph was constructed from a module for full binding graph
|
|
|
* validation.
|
|
|
*
|
|
|
* @deprecated use {@link #isFullBindingGraph()} to tell if this is a full binding graph, or
|
|
|
* {@link ComponentNode#isRealComponent() rootComponentNode().isRealComponent()} to tell if
|
|
|
* the root component node is really a component or derived from a module. Dagger can generate
|
|
|
* full binding graphs for components and subcomponents as well as modules.
|
|
|
*/
|
|
|
@Deprecated
|
|
|
public boolean isModuleBindingGraph() {
|
|
|
return !rootComponentNode().isRealComponent();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns {@code true} if this is a full binding graph, which contains all bindings installed in
|
|
|
* the component, or {@code false} if it is a reachable binding graph, which contains only
|
|
|
* bindings that are reachable from at least one {@linkplain #entryPointEdges() entry point}.
|
|
|
*
|
|
|
* @see <a href="https://dagger.dev/compiler-options#full-binding-graph-validation">Full binding
|
|
|
* graph validation</a>
|
|
|
*/
|
|
|
public abstract boolean isFullBindingGraph();
|
|
|
|
|
|
/**
|
|
|
* Returns {@code true} if the {@link #rootComponentNode()} is a subcomponent. This occurs in
|
|
|
* when {@code -Adagger.fullBindingGraphValidation} is used in a compilation with a subcomponent.
|
|
|
*
|
|
|
* @deprecated use {@link ComponentNode#isSubcomponent() rootComponentNode().isSubcomponent()}
|
|
|
* instead
|
|
|
*/
|
|
|
@Deprecated
|
|
|
public boolean isPartialBindingGraph() {
|
|
|
return rootComponentNode().isSubcomponent();
|
|
|
}
|
|
|
|
|
|
/** Returns the bindings. */
|
|
|
public ImmutableSet<Binding> bindings() {
|
|
|
return nodes(Binding.class);
|
|
|
}
|
|
|
|
|
|
/** Returns the bindings for a key. */
|
|
|
public ImmutableSet<Binding> bindings(Key key) {
|
|
|
return nodes(Binding.class).stream()
|
|
|
.filter(binding -> binding.key().equals(key))
|
|
|
.collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/** Returns the nodes that represent missing bindings. */
|
|
|
public ImmutableSet<MissingBinding> missingBindings() {
|
|
|
return nodes(MissingBinding.class);
|
|
|
}
|
|
|
|
|
|
/** Returns the component nodes. */
|
|
|
public ImmutableSet<ComponentNode> componentNodes() {
|
|
|
return nodes(ComponentNode.class);
|
|
|
}
|
|
|
|
|
|
/** Returns the component node for a component. */
|
|
|
public Optional<ComponentNode> componentNode(ComponentPath component) {
|
|
|
return componentNodes().stream()
|
|
|
.filter(node -> node.componentPath().equals(component))
|
|
|
.findFirst();
|
|
|
}
|
|
|
|
|
|
/** Returns the component nodes for a component. */
|
|
|
public ImmutableSet<ComponentNode> componentNodes(TypeElement component) {
|
|
|
return componentNodes().stream()
|
|
|
.filter(node -> node.componentPath().currentComponent().equals(component))
|
|
|
.collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/** Returns the component node for the root component. */
|
|
|
public ComponentNode rootComponentNode() {
|
|
|
return componentNodes().stream()
|
|
|
.filter(node -> node.componentPath().atRoot())
|
|
|
.findFirst()
|
|
|
.get();
|
|
|
}
|
|
|
|
|
|
/** Returns the dependency edges. */
|
|
|
public ImmutableSet<DependencyEdge> dependencyEdges() {
|
|
|
return dependencyEdgeStream().collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the dependency edges for the dependencies of a binding. For valid graphs, each {@link
|
|
|
* DependencyRequest} will map to a single {@link DependencyEdge}. When conflicting bindings exist
|
|
|
* for a key, the multimap will have several edges for that {@link DependencyRequest}. Graphs that
|
|
|
* have no binding for a key will have an edge whose {@linkplain EndpointPair#target() target
|
|
|
* node} is a {@link MissingBinding}.
|
|
|
*/
|
|
|
public ImmutableSetMultimap<DependencyRequest, DependencyEdge> dependencyEdges(
|
|
|
Binding binding) {
|
|
|
return dependencyEdgeStream(binding)
|
|
|
.collect(toImmutableSetMultimap(DependencyEdge::dependencyRequest, edge -> edge));
|
|
|
}
|
|
|
|
|
|
/** Returns the dependency edges for a dependency request. */
|
|
|
public ImmutableSet<DependencyEdge> dependencyEdges(DependencyRequest dependencyRequest) {
|
|
|
return dependencyEdgeStream()
|
|
|
.filter(edge -> edge.dependencyRequest().equals(dependencyRequest))
|
|
|
.collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the dependency edges for the entry points of a given {@code component}. Each edge's
|
|
|
* source node is that component's component node.
|
|
|
*/
|
|
|
public ImmutableSet<DependencyEdge> entryPointEdges(ComponentPath component) {
|
|
|
return dependencyEdgeStream(componentNode(component).get()).collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
private Stream<DependencyEdge> dependencyEdgeStream(Node node) {
|
|
|
return network().outEdges(node).stream().flatMap(instancesOf(DependencyEdge.class));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the dependency edges for all entry points for all components and subcomponents. Each
|
|
|
* edge's source node is a component node.
|
|
|
*/
|
|
|
public ImmutableSet<DependencyEdge> entryPointEdges() {
|
|
|
return entryPointEdgeStream().collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/** Returns the binding or missing binding nodes that directly satisfy entry points. */
|
|
|
public ImmutableSet<MaybeBinding> entryPointBindings() {
|
|
|
return entryPointEdgeStream()
|
|
|
.map(edge -> (MaybeBinding) network().incidentNodes(edge).target())
|
|
|
.collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the edges for entry points that transitively depend on a binding or missing binding for
|
|
|
* a key.
|
|
|
*/
|
|
|
public ImmutableSet<DependencyEdge> entryPointEdgesDependingOnBinding(
|
|
|
MaybeBinding binding) {
|
|
|
ImmutableNetwork<Node, DependencyEdge> dependencyGraph = dependencyGraph();
|
|
|
Network<Node, DependencyEdge> subgraphDependingOnBinding =
|
|
|
inducedSubgraph(
|
|
|
dependencyGraph, reachableNodes(transpose(dependencyGraph).asGraph(), binding));
|
|
|
return intersection(entryPointEdges(), subgraphDependingOnBinding.edges()).immutableCopy();
|
|
|
}
|
|
|
|
|
|
/** Returns the bindings that directly request a given binding as a dependency. */
|
|
|
public ImmutableSet<Binding> requestingBindings(MaybeBinding binding) {
|
|
|
return network().predecessors(binding).stream()
|
|
|
.flatMap(instancesOf(Binding.class))
|
|
|
.collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the bindings that a given binding directly requests as a dependency. Does not include
|
|
|
* any {@link MissingBinding}s.
|
|
|
*
|
|
|
* @see #requestedMaybeMissingBindings(Binding)
|
|
|
*/
|
|
|
public ImmutableSet<Binding> requestedBindings(Binding binding) {
|
|
|
return network().successors(binding).stream()
|
|
|
.flatMap(instancesOf(Binding.class))
|
|
|
.collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Returns the bindings or missing bindings that a given binding directly requests as a
|
|
|
* dependency.
|
|
|
*
|
|
|
* @see #requestedBindings(Binding)
|
|
|
*/
|
|
|
public ImmutableSet<MaybeBinding> requestedMaybeMissingBindings(Binding binding) {
|
|
|
return network().successors(binding).stream()
|
|
|
.flatMap(instancesOf(MaybeBinding.class))
|
|
|
.collect(toImmutableSet());
|
|
|
}
|
|
|
|
|
|
/** Returns a subnetwork that contains all nodes but only {@link DependencyEdge}s. */
|
|
|
// TODO(dpb): Make public. Cache.
|
|
|
private ImmutableNetwork<Node, DependencyEdge> dependencyGraph() {
|
|
|
MutableNetwork<Node, DependencyEdge> dependencyGraph =
|
|
|
NetworkBuilder.from(network())
|
|
|
.expectedNodeCount(network().nodes().size())
|
|
|
.expectedEdgeCount((int) dependencyEdgeStream().count())
|
|
|
.build();
|
|
|
network().nodes().forEach(dependencyGraph::addNode); // include disconnected nodes
|
|
|
dependencyEdgeStream()
|
|
|
.forEach(
|
|
|
edge -> {
|
|
|
EndpointPair<Node> endpoints = network().incidentNodes(edge);
|
|
|
dependencyGraph.addEdge(endpoints.source(), endpoints.target(), edge);
|
|
|
});
|
|
|
return ImmutableNetwork.copyOf(dependencyGraph);
|
|
|
}
|
|
|
|
|
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
|
|
private <N extends Node> ImmutableSet<N> nodes(Class<N> clazz) {
|
|
|
return (ImmutableSet) nodesByClass().get(clazz);
|
|
|
}
|
|
|
|
|
|
private static final ImmutableSet<Class<? extends Node>> NODE_TYPES =
|
|
|
ImmutableSet.of(Binding.class, MissingBinding.class, ComponentNode.class);
|
|
|
|
|
|
protected ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
|
|
|
return network().nodes().stream()
|
|
|
.collect(
|
|
|
toImmutableSetMultimap(
|
|
|
node ->
|
|
|
NODE_TYPES.stream().filter(clazz -> clazz.isInstance(node)).findFirst().get(),
|
|
|
node -> node));
|
|
|
}
|
|
|
|
|
|
private Stream<DependencyEdge> dependencyEdgeStream() {
|
|
|
return network().edges().stream().flatMap(instancesOf(DependencyEdge.class));
|
|
|
}
|
|
|
|
|
|
private Stream<DependencyEdge> entryPointEdgeStream() {
|
|
|
return dependencyEdgeStream().filter(DependencyEdge::isEntryPoint);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* An edge in the binding graph. Either a {@link DependencyEdge}, a {@link
|
|
|
* ChildFactoryMethodEdge}, or a {@link SubcomponentCreatorBindingEdge}.
|
|
|
*/
|
|
|
public interface Edge {}
|
|
|
|
|
|
/**
|
|
|
* An edge that represents a dependency on a binding.
|
|
|
*
|
|
|
* <p>Because one {@link DependencyRequest} may represent a dependency from two bindings (e.g., a
|
|
|
* dependency of {@code Foo<String>} and {@code Foo<Number>} may have the same key and request
|
|
|
* element), this class does not override {@link #equals(Object)} to use value semantics.
|
|
|
*
|
|
|
* <p>For entry points, the source node is the {@link ComponentNode} that contains the entry
|
|
|
* point. Otherwise the source node is a {@link Binding}.
|
|
|
*
|
|
|
* <p>For dependencies on missing bindings, the target node is a {@link MissingBinding}. Otherwise
|
|
|
* the target node is a {@link Binding}.
|
|
|
*/
|
|
|
public interface DependencyEdge extends Edge {
|
|
|
/** The dependency request. */
|
|
|
DependencyRequest dependencyRequest();
|
|
|
|
|
|
/** Returns {@code true} if this edge represents an entry point. */
|
|
|
boolean isEntryPoint();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* An edge that represents a subcomponent factory method linking a parent component to a child
|
|
|
* subcomponent.
|
|
|
*/
|
|
|
public interface ChildFactoryMethodEdge extends Edge {
|
|
|
/** The subcomponent factory method element. */
|
|
|
ExecutableElement factoryMethod();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* An edge that represents the link between a parent component and a child subcomponent implied by
|
|
|
* a subcomponent creator ({@linkplain dagger.Subcomponent.Builder builder} or {@linkplain
|
|
|
* dagger.Subcomponent.Factory factory}) binding.
|
|
|
*
|
|
|
* <p>The {@linkplain com.google.common.graph.EndpointPair#source() source node} of this edge is a
|
|
|
* {@link Binding} for the subcomponent creator {@link Key} and the {@linkplain
|
|
|
* com.google.common.graph.EndpointPair#target() target node} is a {@link ComponentNode} for the
|
|
|
* child subcomponent.
|
|
|
*/
|
|
|
public interface SubcomponentCreatorBindingEdge extends Edge {
|
|
|
/**
|
|
|
* The modules that {@linkplain Module#subcomponents() declare the subcomponent} that generated
|
|
|
* this edge. Empty if the parent component has a subcomponent creator method and there are no
|
|
|
* declaring modules.
|
|
|
*/
|
|
|
ImmutableSet<TypeElement> declaringModules();
|
|
|
}
|
|
|
|
|
|
/** A node in the binding graph. Either a {@link Binding} or a {@link ComponentNode}. */
|
|
|
// TODO(dpb): Make all the node/edge types top-level.
|
|
|
public interface Node {
|
|
|
/** The component this node belongs to. */
|
|
|
ComponentPath componentPath();
|
|
|
}
|
|
|
|
|
|
/** A node in the binding graph that is either a {@link Binding} or a {@link MissingBinding}. */
|
|
|
public interface MaybeBinding extends Node {
|
|
|
|
|
|
/** The component that owns the binding, or in which the binding is missing. */
|
|
|
@Override
|
|
|
ComponentPath componentPath();
|
|
|
|
|
|
/** The key of the binding, or for which there is no binding. */
|
|
|
Key key();
|
|
|
|
|
|
/** The binding, or empty if missing. */
|
|
|
Optional<Binding> binding();
|
|
|
}
|
|
|
|
|
|
/** A node in the binding graph that represents a missing binding for a key in a component. */
|
|
|
public abstract static class MissingBinding implements MaybeBinding {
|
|
|
/** The component in which the binding is missing. */
|
|
|
@Override
|
|
|
public abstract ComponentPath componentPath();
|
|
|
|
|
|
/** The key for which there is no binding. */
|
|
|
@Override
|
|
|
public abstract Key key();
|
|
|
|
|
|
/** @deprecated This always returns {@code Optional.empty()}. */
|
|
|
@Override
|
|
|
@Deprecated
|
|
|
public Optional<Binding> binding() {
|
|
|
return Optional.empty();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public String toString() {
|
|
|
return String.format("missing binding for %s in %s", key(), componentPath());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* A <b>component node</b> in the graph. Every entry point {@linkplain DependencyEdge dependency
|
|
|
* edge}'s source node is a component node for the component containing the entry point.
|
|
|
*/
|
|
|
public interface ComponentNode extends Node {
|
|
|
|
|
|
/** The component represented by this node. */
|
|
|
@Override
|
|
|
ComponentPath componentPath();
|
|
|
|
|
|
/**
|
|
|
* Returns {@code true} if the component is a {@code @Subcomponent} or
|
|
|
* {@code @ProductionSubcomponent}.
|
|
|
*/
|
|
|
boolean isSubcomponent();
|
|
|
|
|
|
/**
|
|
|
* Returns {@code true} if the component is a real component, or {@code false} if it is a
|
|
|
* fictional component based on a module.
|
|
|
*/
|
|
|
boolean isRealComponent();
|
|
|
|
|
|
/** The entry points on this component. */
|
|
|
ImmutableSet<DependencyRequest> entryPoints();
|
|
|
|
|
|
/** The scopes declared on this component. */
|
|
|
ImmutableSet<Scope> scopes();
|
|
|
}
|
|
|
}
|