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