• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.internal.codegen.binding;
18 
19 import static dagger.internal.codegen.extension.DaggerCollectors.toOptional;
20 import static dagger.internal.codegen.extension.DaggerStreams.presentValues;
21 import static dagger.internal.codegen.extension.DaggerStreams.stream;
22 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
23 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
24 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
25 
26 import com.google.auto.value.AutoValue;
27 import com.google.auto.value.extension.memoized.Memoized;
28 import com.google.common.collect.ImmutableList;
29 import com.google.common.collect.ImmutableMap;
30 import com.google.common.collect.ImmutableSet;
31 import com.google.common.collect.ImmutableSetMultimap;
32 import com.google.common.collect.Sets;
33 import com.google.common.graph.Graphs;
34 import com.google.common.graph.ImmutableNetwork;
35 import com.google.common.graph.Traverser;
36 import dagger.model.BindingGraph.ChildFactoryMethodEdge;
37 import dagger.model.BindingGraph.ComponentNode;
38 import dagger.model.BindingGraph.Edge;
39 import dagger.model.BindingGraph.Node;
40 import dagger.model.ComponentPath;
41 import dagger.model.Key;
42 import java.util.HashMap;
43 import java.util.Map;
44 import java.util.Optional;
45 import javax.lang.model.element.ExecutableElement;
46 import javax.lang.model.element.TypeElement;
47 import javax.lang.model.element.VariableElement;
48 
49 /**
50  * A graph that represents a single component or subcomponent within a fully validated top-level
51  * binding graph.
52  */
53 @AutoValue
54 public abstract class BindingGraph {
55 
56   @AutoValue
57   abstract static class TopLevelBindingGraph extends dagger.model.BindingGraph {
create( ImmutableNetwork<Node, Edge> network, boolean isFullBindingGraph)58     static TopLevelBindingGraph create(
59         ImmutableNetwork<Node, Edge> network, boolean isFullBindingGraph) {
60       TopLevelBindingGraph topLevelBindingGraph =
61           new AutoValue_BindingGraph_TopLevelBindingGraph(network, isFullBindingGraph);
62 
63       ImmutableMap<ComponentPath, ComponentNode> componentNodes =
64           topLevelBindingGraph.componentNodes().stream()
65               .collect(
66                   toImmutableMap(ComponentNode::componentPath, componentNode -> componentNode));
67 
68       ImmutableSetMultimap.Builder<ComponentNode, ComponentNode> subcomponentNodesBuilder =
69           ImmutableSetMultimap.builder();
70       topLevelBindingGraph.componentNodes().stream()
71           .filter(componentNode -> !componentNode.componentPath().atRoot())
72           .forEach(
73               componentNode ->
74                   subcomponentNodesBuilder.put(
75                       componentNodes.get(componentNode.componentPath().parent()), componentNode));
76 
77       // Set these fields directly on the instance rather than passing these in as input to the
78       // AutoValue to prevent exposing this data outside of the class.
79       topLevelBindingGraph.componentNodes = componentNodes;
80       topLevelBindingGraph.subcomponentNodes = subcomponentNodesBuilder.build();
81       return topLevelBindingGraph;
82     }
83 
84     private ImmutableMap<ComponentPath, ComponentNode> componentNodes;
85     private ImmutableSetMultimap<ComponentNode, ComponentNode> subcomponentNodes;
86 
TopLevelBindingGraph()87     TopLevelBindingGraph() {}
88 
89     // This overrides dagger.model.BindingGraph with a more efficient implementation.
90     @Override
componentNode(ComponentPath componentPath)91     public Optional<ComponentNode> componentNode(ComponentPath componentPath) {
92       return componentNodes.containsKey(componentPath)
93           ? Optional.of(componentNodes.get(componentPath))
94           : Optional.empty();
95     }
96 
97     /** Returns the set of subcomponent nodes of the given component node. */
subcomponentNodes(ComponentNode componentNode)98     ImmutableSet<ComponentNode> subcomponentNodes(ComponentNode componentNode) {
99       return subcomponentNodes.get(componentNode);
100     }
101 
102     @Override
103     @Memoized
nodesByClass()104     public ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() {
105       return super.nodesByClass();
106     }
107   }
108 
create( ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph)109   static BindingGraph create(
110       ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph) {
111     return create(Optional.empty(), componentNode, topLevelBindingGraph);
112   }
113 
create( Optional<BindingGraph> parent, ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph)114   private static BindingGraph create(
115       Optional<BindingGraph> parent,
116       ComponentNode componentNode,
117       TopLevelBindingGraph topLevelBindingGraph) {
118     ImmutableSet<BindingNode> reachableBindingNodes =
119         Graphs.reachableNodes(topLevelBindingGraph.network().asGraph(), componentNode).stream()
120             .filter(node -> isSubpath(componentNode.componentPath(), node.componentPath()))
121             .filter(node -> node instanceof BindingNode)
122             .map(node -> (BindingNode) node)
123             .collect(toImmutableSet());
124 
125     // Construct the maps of the ContributionBindings and MembersInjectionBindings.
126     Map<Key, BindingNode> contributionBindings = new HashMap<>();
127     Map<Key, BindingNode> membersInjectionBindings = new HashMap<>();
128     for (BindingNode bindingNode : reachableBindingNodes) {
129       Map<Key, BindingNode> bindingsMap;
130       if (bindingNode.delegate() instanceof ContributionBinding) {
131         bindingsMap = contributionBindings;
132       } else if (bindingNode.delegate() instanceof MembersInjectionBinding) {
133         bindingsMap = membersInjectionBindings;
134       } else {
135         throw new AssertionError("Unexpected binding node type: " + bindingNode.delegate());
136       }
137 
138       // TODO(bcorso): Mapping binding nodes by key is flawed since bindings that depend on local
139       // multibindings can have multiple nodes (one in each component). In this case, we choose the
140       // node in the child-most component since this is likely the node that users of this
141       // BindingGraph will want (and to remain consisted with LegacyBindingGraph). However, ideally
142       // we would avoid this ambiguity by getting dependencies directly from the top-level network.
143       // In particular, rather than using a Binding's list of DependencyRequests (which only
144       // contains the key) we would use the top-level network to find the DependencyEdges for a
145       // particular BindingNode.
146       Key key = bindingNode.key();
147       if (!bindingsMap.containsKey(key)
148           // Always choose the child-most binding node.
149           || bindingNode.componentPath().components().size()
150               > bindingsMap.get(key).componentPath().components().size()) {
151         bindingsMap.put(key, bindingNode);
152       }
153     }
154 
155     BindingGraph bindingGraph = new AutoValue_BindingGraph(componentNode, topLevelBindingGraph);
156 
157     ImmutableSet<ModuleDescriptor> modules =
158         ((ComponentNodeImpl) componentNode).componentDescriptor().modules();
159 
160     ImmutableSet<ModuleDescriptor> inheritedModules =
161         parent.isPresent()
162             ? Sets.union(parent.get().ownedModules, parent.get().inheritedModules).immutableCopy()
163             : ImmutableSet.of();
164 
165     // Set these fields directly on the instance rather than passing these in as input to the
166     // AutoValue to prevent exposing this data outside of the class.
167     bindingGraph.inheritedModules = inheritedModules;
168     bindingGraph.ownedModules = Sets.difference(modules, inheritedModules).immutableCopy();
169     bindingGraph.contributionBindings = ImmutableMap.copyOf(contributionBindings);
170     bindingGraph.membersInjectionBindings = ImmutableMap.copyOf(membersInjectionBindings);
171     bindingGraph.bindingModules =
172         contributionBindings.values().stream()
173             .map(BindingNode::contributingModule)
174             .flatMap(presentValues())
175             .collect(toImmutableSet());
176 
177     return bindingGraph;
178   }
179 
180   private ImmutableMap<Key, BindingNode> contributionBindings;
181   private ImmutableMap<Key, BindingNode> membersInjectionBindings;
182   private ImmutableSet<ModuleDescriptor> inheritedModules;
183   private ImmutableSet<ModuleDescriptor> ownedModules;
184   private ImmutableSet<TypeElement> bindingModules;
185 
BindingGraph()186   BindingGraph() {}
187 
188   /** Returns the {@link ComponentNode} for this graph. */
componentNode()189   public abstract ComponentNode componentNode();
190 
191   /** Returns the {@link ComponentPath} for this graph. */
componentPath()192   public final ComponentPath componentPath() {
193     return componentNode().componentPath();
194   }
195 
196   /** Returns the {@link TopLevelBindingGraph} from which this graph is contained. */
topLevelBindingGraph()197   public abstract TopLevelBindingGraph topLevelBindingGraph();
198 
199   /** Returns the {@link ComponentDescriptor} for this graph */
componentDescriptor()200   public final ComponentDescriptor componentDescriptor() {
201     return ((ComponentNodeImpl) componentNode()).componentDescriptor();
202   }
203 
204   /** Returns the {@link ContributionBinding} for the given {@link Key}. */
contributionBinding(Key key)205   public final ContributionBinding contributionBinding(Key key) {
206     return (ContributionBinding) contributionBindings.get(key).delegate();
207   }
208 
209   /**
210    * Returns the {@link MembersInjectionBinding} for the given {@link Key} or {@link
211    * Optional#empty()} if one does not exist.
212    */
membersInjectionBinding(Key key)213   public final Optional<MembersInjectionBinding> membersInjectionBinding(Key key) {
214     return membersInjectionBindings.containsKey(key)
215         ? Optional.of((MembersInjectionBinding) membersInjectionBindings.get(key).delegate())
216         : Optional.empty();
217   }
218 
219   /** Returns the {@link TypeElement} for the component this graph represents. */
componentTypeElement()220   public final TypeElement componentTypeElement() {
221     return componentPath().currentComponent();
222   }
223 
224   /**
225    * Returns the set of modules that are owned by this graph regardless of whether or not any of
226    * their bindings are used in this graph. For graphs representing top-level {@link
227    * dagger.Component components}, this set will be the same as {@linkplain
228    * ComponentDescriptor#modules() the component's transitive modules}. For {@linkplain Subcomponent
229    * subcomponents}, this set will be the transitive modules that are not owned by any of their
230    * ancestors.
231    */
ownedModuleTypes()232   public final ImmutableSet<TypeElement> ownedModuleTypes() {
233     return ownedModules.stream().map(ModuleDescriptor::moduleElement).collect(toImmutableSet());
234   }
235 
236   /**
237    * Returns the factory method for this subcomponent, if it exists.
238    *
239    * <p>This factory method is the one defined in the parent component's interface.
240    *
241    * <p>In the example below, the {@link BindingGraph#factoryMethod} for {@code ChildComponent}
242    * would return the {@link ExecutableElement}: {@code childComponent(ChildModule1)} .
243    *
244    * <pre><code>
245    *   {@literal @Component}
246    *   interface ParentComponent {
247    *     ChildComponent childComponent(ChildModule1 childModule);
248    *   }
249    * </code></pre>
250    */
251   // TODO(b/73294201): Consider returning the resolved ExecutableType for the factory method.
factoryMethod()252   public final Optional<ExecutableElement> factoryMethod() {
253     return topLevelBindingGraph().network().inEdges(componentNode()).stream()
254         .filter(edge -> edge instanceof ChildFactoryMethodEdge)
255         .map(edge -> ((ChildFactoryMethodEdge) edge).factoryMethod())
256         .collect(toOptional());
257   }
258 
259   /**
260    * Returns a map between the {@linkplain ComponentRequirement component requirement} and the
261    * corresponding {@link VariableElement} for each module parameter in the {@linkplain
262    * BindingGraph#factoryMethod factory method}.
263    */
264   // TODO(dpb): Consider disallowing modules if none of their bindings are used.
factoryMethodParameters()265   public final ImmutableMap<ComponentRequirement, VariableElement> factoryMethodParameters() {
266     return factoryMethod().get().getParameters().stream()
267         .collect(
268             toImmutableMap(
269                 parameter -> ComponentRequirement.forModule(parameter.asType()),
270                 parameter -> parameter));
271   }
272 
273   /**
274    * The types for which the component needs instances.
275    *
276    * <ul>
277    *   <li>component dependencies
278    *   <li>owned modules with concrete instance bindings that are used in the graph
279    *   <li>bound instances
280    * </ul>
281    */
282   @Memoized
componentRequirements()283   public ImmutableSet<ComponentRequirement> componentRequirements() {
284     ImmutableSet<TypeElement> requiredModules =
285         stream(Traverser.forTree(BindingGraph::subgraphs).depthFirstPostOrder(this))
286             .flatMap(graph -> graph.bindingModules.stream())
287             .filter(ownedModuleTypes()::contains)
288             .collect(toImmutableSet());
289     ImmutableSet.Builder<ComponentRequirement> requirements = ImmutableSet.builder();
290     componentDescriptor().requirements().stream()
291         .filter(
292             requirement ->
293                 !requirement.kind().isModule()
294                     || requiredModules.contains(requirement.typeElement()))
295         .forEach(requirements::add);
296     if (factoryMethod().isPresent()) {
297       requirements.addAll(factoryMethodParameters().keySet());
298     }
299     return requirements.build();
300   }
301 
302   /** Returns all {@link ComponentDescriptor}s in the {@link TopLevelBindingGraph}. */
componentDescriptors()303   public final ImmutableSet<ComponentDescriptor> componentDescriptors() {
304     return topLevelBindingGraph().componentNodes().stream()
305         .map(componentNode -> ((ComponentNodeImpl) componentNode).componentDescriptor())
306         .collect(toImmutableSet());
307   }
308 
309   @Memoized
subgraphs()310   public ImmutableList<BindingGraph> subgraphs() {
311     return topLevelBindingGraph().subcomponentNodes(componentNode()).stream()
312         .map(subcomponent -> create(Optional.of(this), subcomponent, topLevelBindingGraph()))
313         .collect(toImmutableList());
314   }
315 
bindingNodes(Key key)316   public final ImmutableSet<BindingNode> bindingNodes(Key key) {
317     ImmutableSet.Builder<BindingNode> builder = ImmutableSet.builder();
318     if (contributionBindings.containsKey(key)) {
319       builder.add(contributionBindings.get(key));
320     }
321     if (membersInjectionBindings.containsKey(key)) {
322       builder.add(membersInjectionBindings.get(key));
323     }
324     return builder.build();
325   }
326 
327   @Memoized
bindingNodes()328   public ImmutableSet<BindingNode> bindingNodes() {
329     return ImmutableSet.<BindingNode>builder()
330         .addAll(contributionBindings.values())
331         .addAll(membersInjectionBindings.values())
332         .build();
333   }
334 
335   // TODO(bcorso): Move this to ComponentPath
isSubpath(ComponentPath path, ComponentPath subpath)336   private static boolean isSubpath(ComponentPath path, ComponentPath subpath) {
337     if (path.components().size() < subpath.components().size()) {
338       return false;
339     }
340     for (int i = 0; i < subpath.components().size(); i++) {
341       if (!path.components().get(i).equals(subpath.components().get(i))) {
342         return false;
343       }
344     }
345     return true;
346   }
347 }
348