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