1 /** 2 * Copyright (C) 2011 Google Inc. 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 com.google.inject.grapher; 18 19 import com.google.common.collect.Lists; 20 import com.google.common.collect.Maps; 21 import com.google.common.collect.Sets; 22 import com.google.inject.Binding; 23 import com.google.inject.Injector; 24 import com.google.inject.Key; 25 import java.io.IOException; 26 import java.util.Iterator; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Set; 30 31 /** 32 * Abstract injector grapher that builds the dependency graph but doesn't render it. 33 * 34 * @author bojand@google.com (Bojan Djordjevic) 35 * @since 4.0 36 */ 37 public abstract class AbstractInjectorGrapher implements InjectorGrapher { 38 private final RootKeySetCreator rootKeySetCreator; 39 private final AliasCreator aliasCreator; 40 private final NodeCreator nodeCreator; 41 private final EdgeCreator edgeCreator; 42 43 /** 44 * Parameters used to override default settings of the grapher. 45 * @since 4.0 46 */ 47 public static final class GrapherParameters { 48 private RootKeySetCreator rootKeySetCreator = new DefaultRootKeySetCreator(); 49 private AliasCreator aliasCreator = new ProviderAliasCreator(); 50 private NodeCreator nodeCreator = new DefaultNodeCreator(); 51 private EdgeCreator edgeCreator = new DefaultEdgeCreator(); 52 getRootKeySetCreator()53 public RootKeySetCreator getRootKeySetCreator() { 54 return rootKeySetCreator; 55 } 56 setRootKeySetCreator(RootKeySetCreator rootKeySetCreator)57 public GrapherParameters setRootKeySetCreator(RootKeySetCreator rootKeySetCreator) { 58 this.rootKeySetCreator = rootKeySetCreator; 59 return this; 60 } 61 getAliasCreator()62 public AliasCreator getAliasCreator() { 63 return aliasCreator; 64 } 65 setAliasCreator(AliasCreator aliasCreator)66 public GrapherParameters setAliasCreator(AliasCreator aliasCreator) { 67 this.aliasCreator = aliasCreator; 68 return this; 69 } 70 getNodeCreator()71 public NodeCreator getNodeCreator() { 72 return nodeCreator; 73 } 74 setNodeCreator(NodeCreator nodeCreator)75 public GrapherParameters setNodeCreator(NodeCreator nodeCreator) { 76 this.nodeCreator = nodeCreator; 77 return this; 78 } 79 getEdgeCreator()80 public EdgeCreator getEdgeCreator() { 81 return edgeCreator; 82 } 83 setEdgeCreator(EdgeCreator edgeCreator)84 public GrapherParameters setEdgeCreator(EdgeCreator edgeCreator) { 85 this.edgeCreator = edgeCreator; 86 return this; 87 } 88 } 89 AbstractInjectorGrapher()90 public AbstractInjectorGrapher() { 91 this(new GrapherParameters()); 92 } 93 AbstractInjectorGrapher(GrapherParameters options)94 public AbstractInjectorGrapher(GrapherParameters options) { 95 this.rootKeySetCreator = options.getRootKeySetCreator(); 96 this.aliasCreator = options.getAliasCreator(); 97 this.nodeCreator = options.getNodeCreator(); 98 this.edgeCreator = options.getEdgeCreator(); 99 } 100 graph(Injector injector)101 @Override public final void graph(Injector injector) throws IOException { 102 graph(injector, rootKeySetCreator.getRootKeys(injector)); 103 } 104 graph(Injector injector, Set<Key<?>> root)105 @Override public final void graph(Injector injector, Set<Key<?>> root) throws IOException { 106 reset(); 107 108 Iterable<Binding<?>> bindings = getBindings(injector, root); 109 Map<NodeId, NodeId> aliases = resolveAliases(aliasCreator.createAliases(bindings)); 110 createNodes(nodeCreator.getNodes(bindings), aliases); 111 createEdges(edgeCreator.getEdges(bindings), aliases); 112 postProcess(); 113 } 114 115 /** Resets the state of the grapher before rendering a new graph. */ reset()116 protected abstract void reset() throws IOException; 117 118 /** Adds a new interface node to the graph. */ newInterfaceNode(InterfaceNode node)119 protected abstract void newInterfaceNode(InterfaceNode node) throws IOException; 120 121 /** Adds a new implementation node to the graph. */ newImplementationNode(ImplementationNode node)122 protected abstract void newImplementationNode(ImplementationNode node) throws IOException; 123 124 /** Adds a new instance node to the graph. */ newInstanceNode(InstanceNode node)125 protected abstract void newInstanceNode(InstanceNode node) throws IOException; 126 127 /** Adds a new dependency edge to the graph. */ newDependencyEdge(DependencyEdge edge)128 protected abstract void newDependencyEdge(DependencyEdge edge) throws IOException; 129 130 /** Adds a new binding edge to the graph. */ newBindingEdge(BindingEdge edge)131 protected abstract void newBindingEdge(BindingEdge edge) throws IOException; 132 133 /** Performs any post processing required after all nodes and edges have been added. */ postProcess()134 protected abstract void postProcess() throws IOException; 135 createNodes(Iterable<Node> nodes, Map<NodeId, NodeId> aliases)136 private void createNodes(Iterable<Node> nodes, Map<NodeId, NodeId> aliases) throws IOException { 137 for (Node node : nodes) { 138 NodeId originalId = node.getId(); 139 NodeId resolvedId = resolveAlias(aliases, originalId); 140 node = node.copy(resolvedId); 141 142 // Only render nodes that aren't aliased to some other node. 143 if (resolvedId.equals(originalId)) { 144 if (node instanceof InterfaceNode) { 145 newInterfaceNode((InterfaceNode) node); 146 } else if (node instanceof ImplementationNode) { 147 newImplementationNode((ImplementationNode) node); 148 } else { 149 newInstanceNode((InstanceNode) node); 150 } 151 } 152 } 153 } 154 createEdges(Iterable<Edge> edges, Map<NodeId, NodeId> aliases)155 private void createEdges(Iterable<Edge> edges, Map<NodeId, NodeId> aliases) throws IOException { 156 for (Edge edge : edges) { 157 edge = edge.copy(resolveAlias(aliases, edge.getFromId()), 158 resolveAlias(aliases, edge.getToId())); 159 if (!edge.getFromId().equals(edge.getToId())) { 160 if (edge instanceof BindingEdge) { 161 newBindingEdge((BindingEdge) edge); 162 } else { 163 newDependencyEdge((DependencyEdge) edge); 164 } 165 } 166 } 167 } 168 resolveAlias(Map<NodeId, NodeId> aliases, NodeId nodeId)169 private NodeId resolveAlias(Map<NodeId, NodeId> aliases, NodeId nodeId) { 170 return aliases.containsKey(nodeId) ? aliases.get(nodeId) : nodeId; 171 } 172 173 /** 174 * Transitively resolves aliases. Given aliases (X to Y) and (Y to Z), it will return mappings 175 * (X to Z) and (Y to Z). 176 */ resolveAliases(Iterable<Alias> aliases)177 private Map<NodeId, NodeId> resolveAliases(Iterable<Alias> aliases) { 178 Map<NodeId, NodeId> resolved = Maps.newHashMap(); 179 Map<NodeId, Set<NodeId>> inverse = Maps.newHashMap(); 180 181 for (Alias alias : aliases) { 182 NodeId from = alias.getFromId(); 183 NodeId to = alias.getToId(); 184 if (resolved.containsKey(to)) { 185 to = resolved.get(to); 186 } 187 resolved.put(from, to); 188 if (inverse.get(to) == null) { 189 inverse.put(to, Sets.<NodeId>newHashSet()); 190 } 191 inverse.get(to).add(from); 192 193 Set<NodeId> prev = inverse.get(from); 194 if (prev != null) { 195 for (NodeId id : prev) { 196 resolved.remove(id); 197 inverse.get(from).remove(id); 198 resolved.put(id, to); 199 inverse.get(to).add(id); 200 } 201 } 202 } 203 204 return resolved; 205 } 206 207 /** Returns the bindings for the root keys and their transitive dependencies. */ getBindings(Injector injector, Set<Key<?>> root)208 private Iterable<Binding<?>> getBindings(Injector injector, Set<Key<?>> root) { 209 Set<Key<?>> keys = Sets.newHashSet(root); 210 Set<Key<?>> visitedKeys = Sets.newHashSet(); 211 List<Binding<?>> bindings = Lists.newArrayList(); 212 TransitiveDependencyVisitor keyVisitor = new TransitiveDependencyVisitor(); 213 214 while (!keys.isEmpty()) { 215 Iterator<Key<?>> iterator = keys.iterator(); 216 Key<?> key = iterator.next(); 217 iterator.remove(); 218 219 if (!visitedKeys.contains(key)) { 220 Binding<?> binding = injector.getBinding(key); 221 bindings.add(binding); 222 visitedKeys.add(key); 223 keys.addAll(binding.acceptTargetVisitor(keyVisitor)); 224 } 225 } 226 return bindings; 227 } 228 } 229