Refactoring of guice-grapher
- makes it possible to inject non-scoped graphers
- fixes http://code.google.com/p/google-guice/issues/detail?id=489 (in
the sense that the exposed node is graphed, it still doesn't follow
private bindings)
- simplifies implementation classes:
- aliasing, node creating and edge creating are done as three
independent steps
- implementation classes will get aliased nodes so they don't need to
worry about aliasing
- edges are always created after the nodes they link
- removes boilerplate code to build node and edge factories
Revision created by MOE tool push_codebase.
MOE_MIGRATION=2701
git-svn-id: https://google-guice.googlecode.com/svn/trunk@1575 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/extensions/grapher/src/com/google/inject/grapher/AbstractInjectorGrapher.java b/extensions/grapher/src/com/google/inject/grapher/AbstractInjectorGrapher.java
new file mode 100644
index 0000000..da90ba3
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/AbstractInjectorGrapher.java
@@ -0,0 +1,224 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.inject.Binding;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Abstract injector grapher that builds the dependency graph but doesn't render it.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public abstract class AbstractInjectorGrapher implements InjectorGrapher {
+ private final RootKeySetCreator rootKeySetCreator;
+ private final AliasCreator aliasCreator;
+ private final NodeCreator nodeCreator;
+ private final EdgeCreator edgeCreator;
+
+ /** Parameters used to override default settings of the grapher. */
+ public static final class GrapherParameters {
+ private RootKeySetCreator rootKeySetCreator = new DefaultRootKeySetCreator();
+ private AliasCreator aliasCreator = new ProviderAliasCreator();
+ private NodeCreator nodeCreator = new DefaultNodeCreator();
+ private EdgeCreator edgeCreator = new DefaultEdgeCreator();
+
+ public RootKeySetCreator getRootKeySetCreator() {
+ return rootKeySetCreator;
+ }
+
+ public GrapherParameters setRootKeySetCreator(RootKeySetCreator rootKeySetCreator) {
+ this.rootKeySetCreator = rootKeySetCreator;
+ return this;
+ }
+
+ public AliasCreator getAliasCreator() {
+ return aliasCreator;
+ }
+
+ public GrapherParameters setAliasCreator(AliasCreator aliasCreator) {
+ this.aliasCreator = aliasCreator;
+ return this;
+ }
+
+ public NodeCreator getNodeCreator() {
+ return nodeCreator;
+ }
+
+ public GrapherParameters setNodeCreator(NodeCreator nodeCreator) {
+ this.nodeCreator = nodeCreator;
+ return this;
+ }
+
+ public EdgeCreator getEdgeCreator() {
+ return edgeCreator;
+ }
+
+ public GrapherParameters setEdgeCreator(EdgeCreator edgeCreator) {
+ this.edgeCreator = edgeCreator;
+ return this;
+ }
+ }
+
+ public AbstractInjectorGrapher() {
+ this(new GrapherParameters());
+ }
+
+ public AbstractInjectorGrapher(GrapherParameters options) {
+ this.rootKeySetCreator = options.getRootKeySetCreator();
+ this.aliasCreator = options.getAliasCreator();
+ this.nodeCreator = options.getNodeCreator();
+ this.edgeCreator = options.getEdgeCreator();
+ }
+
+ @Override public final void graph(Injector injector) throws IOException {
+ graph(injector, rootKeySetCreator.getRootKeys(injector));
+ }
+
+ @Override public final void graph(Injector injector, Set<Key<?>> root) throws IOException {
+ reset();
+
+ Iterable<Binding<?>> bindings = getBindings(injector, root);
+ Map<NodeId, NodeId> aliases = resolveAliases(aliasCreator.createAliases(bindings));
+ createNodes(nodeCreator.getNodes(bindings), aliases);
+ createEdges(edgeCreator.getEdges(bindings), aliases);
+ postProcess();
+ }
+
+ /** Resets the state of the grapher before rendering a new graph. */
+ protected abstract void reset() throws IOException;
+
+ /** Adds a new interface node to the graph. */
+ protected abstract void newInterfaceNode(InterfaceNode node) throws IOException;
+
+ /** Adds a new implementation node to the graph. */
+ protected abstract void newImplementationNode(ImplementationNode node) throws IOException;
+
+ /** Adds a new instance node to the graph. */
+ protected abstract void newInstanceNode(InstanceNode node) throws IOException;
+
+ /** Adds a new dependency edge to the graph. */
+ protected abstract void newDependencyEdge(DependencyEdge edge) throws IOException;
+
+ /** Adds a new binding edge to the graph. */
+ protected abstract void newBindingEdge(BindingEdge edge) throws IOException;
+
+ /** Performs any post processing required after all nodes and edges have been added. */
+ protected abstract void postProcess() throws IOException;
+
+ private void createNodes(Iterable<Node> nodes, Map<NodeId, NodeId> aliases) throws IOException {
+ for (Node node : nodes) {
+ NodeId originalId = node.getId();
+ NodeId resolvedId = resolveAlias(aliases, originalId);
+ node = node.copy(resolvedId);
+
+ // Only render nodes that aren't aliased to some other node.
+ if (resolvedId.equals(originalId)) {
+ if (node instanceof InterfaceNode) {
+ newInterfaceNode((InterfaceNode) node);
+ } else if (node instanceof ImplementationNode) {
+ newImplementationNode((ImplementationNode) node);
+ } else {
+ newInstanceNode((InstanceNode) node);
+ }
+ }
+ }
+ }
+
+ private void createEdges(Iterable<Edge> edges, Map<NodeId, NodeId> aliases) throws IOException {
+ for (Edge edge : edges) {
+ edge = edge.copy(resolveAlias(aliases, edge.getFromId()),
+ resolveAlias(aliases, edge.getToId()));
+ if (!edge.getFromId().equals(edge.getToId())) {
+ if (edge instanceof BindingEdge) {
+ newBindingEdge((BindingEdge) edge);
+ } else {
+ newDependencyEdge((DependencyEdge) edge);
+ }
+ }
+ }
+ }
+
+ private NodeId resolveAlias(Map<NodeId, NodeId> aliases, NodeId nodeId) {
+ return aliases.containsKey(nodeId) ? aliases.get(nodeId) : nodeId;
+ }
+
+ /**
+ * Transitively resolves aliases. Given aliases (X to Y) and (Y to Z), it will return mappings
+ * (X to Z) and (Y to Z).
+ */
+ private Map<NodeId, NodeId> resolveAliases(Iterable<Alias> aliases) {
+ Map<NodeId, NodeId> resolved = Maps.newHashMap();
+ Map<NodeId, Set<NodeId>> inverse = Maps.newHashMap();
+
+ for (Alias alias : aliases) {
+ NodeId from = alias.getFromId();
+ NodeId to = alias.getToId();
+ if (resolved.containsKey(to)) {
+ to = resolved.get(to);
+ }
+ resolved.put(from, to);
+ if (inverse.get(to) == null) {
+ inverse.put(to, Sets.<NodeId>newHashSet());
+ }
+ inverse.get(to).add(from);
+
+ Set<NodeId> prev = inverse.get(from);
+ if (prev != null) {
+ for (NodeId id : prev) {
+ resolved.remove(id);
+ inverse.get(from).remove(id);
+ resolved.put(id, to);
+ inverse.get(to).add(id);
+ }
+ }
+ }
+
+ return resolved;
+ }
+
+ /** Returns the bindings for the root keys and their transitive dependencies. */
+ private Iterable<Binding<?>> getBindings(Injector injector, Set<Key<?>> root) {
+ Set<Key<?>> keys = Sets.newHashSet(root);
+ Set<Key<?>> visitedKeys = Sets.newHashSet();
+ List<Binding<?>> bindings = Lists.newArrayList();
+ TransitiveDependencyVisitor keyVisitor = new TransitiveDependencyVisitor();
+
+ while (!keys.isEmpty()) {
+ Iterator<Key<?>> iterator = keys.iterator();
+ Key<?> key = iterator.next();
+ iterator.remove();
+
+ if (!visitedKeys.contains(key)) {
+ Binding<?> binding = injector.getBinding(key);
+ bindings.add(binding);
+ visitedKeys.add(key);
+ keys.addAll(binding.acceptTargetVisitor(keyVisitor));
+ }
+ }
+ return bindings;
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/Alias.java b/extensions/grapher/src/com/google/inject/grapher/Alias.java
new file mode 100644
index 0000000..eb68736
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/Alias.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+/**
+ * Alias between two nodes. Causes the 'from' node to be aliased with the 'to' node, which means
+ * that the 'from' node is not rendered and all edges going to it instead go to the 'to' node.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public final class Alias {
+ private final NodeId fromId;
+ private final NodeId toId;
+
+ public Alias(NodeId fromId, NodeId toId) {
+ this.fromId = fromId;
+ this.toId = toId;
+ }
+
+ public NodeId getFromId() {
+ return fromId;
+ }
+
+ public NodeId getToId() {
+ return toId;
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/AliasCreator.java b/extensions/grapher/src/com/google/inject/grapher/AliasCreator.java
new file mode 100644
index 0000000..db9751e
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/AliasCreator.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.inject.Binding;
+
+/**
+ * Creator of node aliases. Used by dependency graphers to merge nodes in the internal Guice graph
+ * into a single node on the rendered graph.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public interface AliasCreator {
+ /**
+ * Returns aliases for the given dependency graph. The aliases do not need to be transitively
+ * resolved, i.e. it is valid to return an alias (X to Y) and an alias (Y to Z). It is the
+ * responsibility of the caller to resolve this to (X to Z) and (Y to Z).
+ *
+ * @param bindings bindings that make up the dependency graph
+ * @return aliases that should be applied on the graph
+ */
+ Iterable<Alias> createAliases(Iterable<Binding<?>> bindings);
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/BindingEdge.java b/extensions/grapher/src/com/google/inject/grapher/BindingEdge.java
index 3ec9e79..9984f19 100644
--- a/extensions/grapher/src/com/google/inject/grapher/BindingEdge.java
+++ b/extensions/grapher/src/com/google/inject/grapher/BindingEdge.java
@@ -16,19 +16,18 @@
package com.google.inject.grapher;
+import com.google.common.base.Objects;
+
/**
- * Interface for an edge that connects an interface to the type or instance
- * that is bound to implement it.
+ * Edge that connects an interface to the type or instance that is bound to implement it.
*
* @author phopkins@gmail.com (Pete Hopkins)
- *
- * @param <K> The type for node IDs.
*/
-public interface BindingEdge<K> {
+public class BindingEdge extends Edge {
/**
* Classification for what kind of binding this edge represents.
*/
- enum Type {
+ public enum Type {
/** Binding is to an instance or class of the binding's same type. */
NORMAL,
/** Binding is to an instance or class that provides the binding's type. */
@@ -37,23 +36,34 @@
CONVERTED_CONSTANT
}
- void setType(Type type);
+ private final Type type;
- /**
- * Factory interface for {@link BindingEdge}s. Renderer implementations will
- * need to provide an implementation for this.
- *
- * @param <K> The type for node IDs.
- * @param <T> The {@link BindingEdge} sub-type that this factory provides.
- */
- interface Factory<K, T extends BindingEdge<K>> {
- /**
- * Creates a new {@link BindingEdge} instance and adds it to the graph.
- *
- * @param fromId Node ID for the interface node.
- * @param toId Node ID for the implementation (class or instance) node.
- * @return The newly created and added {@link BindingEdge}.
- */
- T newBindingEdge(K fromId, K toId);
+ public BindingEdge(NodeId fromId, NodeId toId, Type type) {
+ super(fromId, toId);
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof BindingEdge)) {
+ return false;
+ }
+ BindingEdge other = (BindingEdge) obj;
+ return super.equals(other) && Objects.equal(type, other.type);
+ }
+
+ @Override public int hashCode() {
+ return 31 * super.hashCode() + Objects.hashCode(type);
+ }
+
+ @Override public String toString() {
+ return "BindingEdge{fromId=" + getFromId() + " toId=" + getToId() + " type=" + type + "}";
+ }
+
+ @Override public Edge copy(NodeId fromId, NodeId toId) {
+ return new BindingEdge(fromId, toId, type);
}
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/DefaultEdgeCreator.java b/extensions/grapher/src/com/google/inject/grapher/DefaultEdgeCreator.java
new file mode 100644
index 0000000..5ba402d
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/DefaultEdgeCreator.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.inject.Binding;
+import com.google.inject.spi.ConstructorBinding;
+import com.google.inject.spi.ConvertedConstantBinding;
+import com.google.inject.spi.DefaultBindingTargetVisitor;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.spi.InstanceBinding;
+import com.google.inject.spi.LinkedKeyBinding;
+import com.google.inject.spi.ProviderBinding;
+import com.google.inject.spi.ProviderInstanceBinding;
+import com.google.inject.spi.ProviderKeyBinding;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Default edge creator.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+final class DefaultEdgeCreator implements EdgeCreator {
+
+ @Override public Iterable<Edge> getEdges(Iterable<Binding<?>> bindings) {
+ List<Edge> edges = Lists.newArrayList();
+ EdgeVisitor visitor = new EdgeVisitor();
+ for (Binding<?> binding : bindings) {
+ edges.addAll(binding.acceptTargetVisitor(visitor));
+ }
+ return edges;
+ }
+
+ /**
+ * {@link BindingTargetVisitor} that adds edges to the graph based on the visited {@link Binding}.
+ */
+ private static final class EdgeVisitor
+ extends DefaultBindingTargetVisitor<Object, Collection<Edge>> {
+
+ /**
+ * Returns a dependency edge for each {@link Dependency} in the binding. These will be from the
+ * given node ID to the {@link Dependency}'s {@link Key}.
+ *
+ * @param nodeId ID of the node that should be the tail of the dependency
+ * edges
+ * @param binding {@link Binding} for the dependencies
+ */
+ private <T extends Binding<?> & HasDependencies> Collection<Edge> newDependencyEdges(
+ NodeId nodeId, T binding) {
+ ImmutableList.Builder<Edge> builder = ImmutableList.builder();
+ for (Dependency<?> dependency : binding.getDependencies()) {
+ NodeId to = NodeId.newTypeId(dependency.getKey());
+ builder.add(new DependencyEdge(nodeId, to, dependency.getInjectionPoint()));
+ }
+ return builder.build();
+ }
+
+ /**
+ * Visitor for {@link ConstructorBinding}s. These are for classes that Guice will instantiate to
+ * satisfy injection requests.
+ */
+ @Override public Collection<Edge> visit(ConstructorBinding<?> binding) {
+ return newDependencyEdges(NodeId.newTypeId(binding.getKey()), binding);
+ }
+
+ /**
+ * Visitor for {@link ConvertedConstantBinding}. The {@link Binding}'s {@link Key} will be of an
+ * annotated primitive type, and the value of {@link ConvertedConstantBinding#getSourceKey()}
+ * will be of a {@link String} with the same annotation.
+ */
+ @Override public Collection<Edge> visit(ConvertedConstantBinding<?> binding) {
+ return ImmutableList.<Edge>of(new BindingEdge(NodeId.newTypeId(binding.getKey()),
+ NodeId.newTypeId(binding.getSourceKey()),
+ BindingEdge.Type.CONVERTED_CONSTANT));
+ }
+
+ /**
+ * Visitor for {@link InstanceBinding}. We then render any dependency edgess that the instance
+ * may have, which come either from {@link InjectionPoint}s (method and field) on the instance,
+ * or on {@link Dependency}s the instance declares through the {@link HasDependencies}
+ * interface.
+ */
+ @Override public Collection<Edge> visit(InstanceBinding<?> binding) {
+ return new ImmutableList.Builder<Edge>()
+ .add(new BindingEdge(NodeId.newTypeId(binding.getKey()),
+ NodeId.newInstanceId(binding.getKey()),
+ BindingEdge.Type.NORMAL))
+ .addAll(newDependencyEdges(NodeId.newInstanceId(binding.getKey()), binding))
+ .build();
+ }
+
+ /**
+ * Visitor for {@link LinkedKeyBinding}. This is the standard {@link Binding} you get from
+ * binding an interface class to an implementation class. We draw a {@link BindingEdge} from
+ * the interface node to the node of the implementing class.
+ */
+ @Override public Collection<Edge> visit(LinkedKeyBinding<?> binding) {
+ return ImmutableList.<Edge>of(new BindingEdge(NodeId.newTypeId(binding.getKey()),
+ NodeId.newTypeId(binding.getLinkedKey()),
+ BindingEdge.Type.NORMAL));
+ }
+
+ /**
+ * Visitor for {@link ProviderBinding}. These {@link Binding}s arise from an
+ * {@link InjectionPoint} for the {@link Provider} interface.
+ */
+ @Override public Collection<Edge> visit(ProviderBinding<?> binding) {
+ return ImmutableList.<Edge>of(new BindingEdge(NodeId.newTypeId(binding.getKey()),
+ NodeId.newTypeId(binding.getProvidedKey()), BindingEdge.Type.PROVIDER));
+ }
+
+ /**
+ * Same as {@link #visit(InstanceBinding)}, but the binding edge is
+ * {@link BindingEdge.Type#PROVIDER}.
+ */
+ @Override public Collection<Edge> visit(ProviderInstanceBinding<?> binding) {
+ return new ImmutableList.Builder<Edge>()
+ .add(new BindingEdge(NodeId.newTypeId(binding.getKey()),
+ NodeId.newInstanceId(binding.getKey()), BindingEdge.Type.PROVIDER))
+ .addAll(newDependencyEdges(NodeId.newInstanceId(binding.getKey()), binding))
+ .build();
+ }
+
+ /**
+ * Same as {@link #visit(LinkedKeyBinding)}, but the binding edge is
+ * {@link BindingEdge.Type#PROVIDER}.
+ */
+ @Override public Collection<Edge> visit(ProviderKeyBinding<?> binding) {
+ return ImmutableList.<Edge>of(new BindingEdge(NodeId.newTypeId(binding.getKey()),
+ NodeId.newTypeId(binding.getProviderKey()), BindingEdge.Type.PROVIDER));
+ }
+
+ @Override public Collection<Edge> visitOther(Binding<?> binding) {
+ return ImmutableList.of();
+ }
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/DefaultNodeCreator.java b/extensions/grapher/src/com/google/inject/grapher/DefaultNodeCreator.java
new file mode 100644
index 0000000..557e850
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/DefaultNodeCreator.java
@@ -0,0 +1,130 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.inject.Binding;
+import com.google.inject.spi.ConstructorBinding;
+import com.google.inject.spi.DefaultBindingTargetVisitor;
+import com.google.inject.spi.Dependency;
+import com.google.inject.spi.HasDependencies;
+import com.google.inject.spi.InjectionPoint;
+import com.google.inject.spi.InstanceBinding;
+import com.google.inject.spi.ProviderInstanceBinding;
+import java.lang.reflect.Member;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Default node creator.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+final class DefaultNodeCreator implements NodeCreator {
+ @Override public Iterable<Node> getNodes(Iterable<Binding<?>> bindings) {
+ List<Node> nodes = Lists.newArrayList();
+ NodeVisitor visitor = new NodeVisitor();
+ for (Binding<?> binding : bindings) {
+ nodes.addAll(binding.acceptTargetVisitor(visitor));
+ }
+ return nodes;
+ }
+
+ /**
+ * {@link BindingTargetVisitor} that adds nodes to the graph based on the visited {@link Binding}.
+ */
+ private static final class NodeVisitor
+ extends DefaultBindingTargetVisitor<Object, Collection<Node>> {
+
+ /** Returns a new interface node for the given {@link Binding}. */
+ private InterfaceNode newInterfaceNode(Binding<?> binding) {
+ return new InterfaceNode(NodeId.newTypeId(binding.getKey()), binding.getSource());
+ }
+
+ /**
+ * Returns a new implementation node for the given binding.
+ *
+ * @param binding binding for the node to create
+ * @param members members to add to the node
+ * @return implementation node for the given binding
+ */
+ private ImplementationNode newImplementationNode(Binding<?> binding,
+ Collection<Member> members) {
+ return new ImplementationNode(NodeId.newTypeId(binding.getKey()), binding.getSource(),
+ members);
+ }
+
+ /**
+ * Returns a new instance node for the given {@link Binding}.
+ *
+ * @param binding binding for the node to create
+ * @param instance value of the instance
+ * @return instance node for the given binding
+ */
+ private <T extends Binding<?> & HasDependencies> InstanceNode newInstanceNode(T binding,
+ Object instance) {
+ Collection<Member> members = Lists.newArrayList();
+ for (Dependency<?> dependency : binding.getDependencies()) {
+ InjectionPoint injectionPoint = dependency.getInjectionPoint();
+
+ if (injectionPoint != null) {
+ members.add(injectionPoint.getMember());
+ }
+ }
+ return new InstanceNode(NodeId.newInstanceId(binding.getKey()), binding.getSource(), instance,
+ members);
+ }
+
+ /**
+ * Visitor for {@link ConstructorBinding}s. These are for classes that Guice will instantiate to
+ * satisfy injection requests.
+ */
+ @Override public Collection<Node> visit(ConstructorBinding<?> binding) {
+ Collection<Member> members = Lists.newArrayList();
+ members.add(binding.getConstructor().getMember());
+ for (InjectionPoint injectionPoint : binding.getInjectableMembers()) {
+ members.add(injectionPoint.getMember());
+ }
+
+ return ImmutableList.<Node>of(newImplementationNode(binding, members));
+ }
+
+ /**
+ * Visitor for {@link InstanceBinding}. We render two nodes in this case: an interface node for
+ * the binding's {@link Key}, and then an implementation node for the instance {@link Object}
+ * itself.
+ */
+ @Override public Collection<Node> visit(InstanceBinding<?> binding) {
+ return ImmutableList.<Node>of(newInterfaceNode(binding), newInstanceNode(binding,
+ binding.getInstance()));
+ }
+
+ /**
+ * Same as {@link #visit(InstanceBinding)}, but the binding edge is
+ * {@link BindingEdgeType#PROVIDER}.
+ */
+ @Override public Collection<Node> visit(ProviderInstanceBinding<?> binding) {
+ return ImmutableList.<Node>of(newInterfaceNode(binding), newInstanceNode(binding,
+ binding.getProviderInstance()));
+ }
+
+ @Override public Collection<Node> visitOther(Binding<?> binding) {
+ return ImmutableList.<Node>of(newInterfaceNode(binding));
+ }
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/DefaultRootKeySetCreator.java b/extensions/grapher/src/com/google/inject/grapher/DefaultRootKeySetCreator.java
new file mode 100644
index 0000000..ce73f17
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/DefaultRootKeySetCreator.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.collect.Sets;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * Root key set creator that starts with all types that are not Guice internal types or the
+ * {@link Logger} type.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public class DefaultRootKeySetCreator implements RootKeySetCreator {
+ private static final Key<Logger> loggerKey = Key.get(Logger.class);
+
+ @Override public Set<Key<?>> getRootKeys(Injector injector) {
+ Set<Key<?>> root = Sets.newHashSet();
+ for (Key<?> key : injector.getBindings().keySet()) {
+ if (key.getTypeLiteral().getRawType().getPackage() != Guice.class.getPackage()
+ && !loggerKey.equals(key)) {
+ root.add(key);
+ }
+ }
+ return root;
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/DependencyEdge.java b/extensions/grapher/src/com/google/inject/grapher/DependencyEdge.java
index 5b454a7..94f6316 100644
--- a/extensions/grapher/src/com/google/inject/grapher/DependencyEdge.java
+++ b/extensions/grapher/src/com/google/inject/grapher/DependencyEdge.java
@@ -16,34 +16,49 @@
package com.google.inject.grapher;
+import com.google.common.base.Objects;
import com.google.inject.spi.InjectionPoint;
/**
- * Interface for an edge from a class or {@link InjectionPoint} to the
- * interface node that will satisfy the dependency.
+ * Edge from a class or {@link InjectionPoint} to the interface node that will satisfy the
+ * dependency.
*
* @author phopkins@gmail.com (Pete Hopkins)
- *
- * @param <K> The type for node IDs.
*/
-public interface DependencyEdge<K> {
+public class DependencyEdge extends Edge {
/**
- * Factory interface for {@link DependencyEdge}s. Renderer implementations
- * will need to provide an implementation for this.
- *
- * @param <K> The type for node IDs.
- * @param <T> The {@link DependencyEdge} sub-type that this factory provides.
+ * Injection point to which this dependency belongs, or null if the dependency isn't attached to a
+ * particular injection point.
*/
- interface Factory<K, T extends DependencyEdge<K>> {
- /**
- * Creates a new {@link DependencyEdge} and adds it to the graph.
- *
- * @param fromId The ID for the class or instance node that has the
- * dependency.
- * @param fromPoint The point where the dependency will be
- * {@literal @}{@link Inject}ed.
- * @param toId The ID for the interface node that satisfies the dependency.
- */
- T newDependencyEdge(K fromId, InjectionPoint fromPoint, K toId);
+ private final InjectionPoint injectionPoint;
+
+ public DependencyEdge(NodeId fromId, NodeId toId, InjectionPoint injectionPoint) {
+ super(fromId, toId);
+ this.injectionPoint = injectionPoint;
+ }
+
+ public InjectionPoint getInjectionPoint() {
+ return injectionPoint;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof DependencyEdge)) {
+ return false;
+ }
+ DependencyEdge other = (DependencyEdge) obj;
+ return super.equals(other) && Objects.equal(injectionPoint, other.injectionPoint);
+ }
+
+ @Override public int hashCode() {
+ return 31 * super.hashCode() + Objects.hashCode(injectionPoint);
+ }
+
+ @Override public String toString() {
+ return "DependencyEdge{fromId=" + getFromId() + " toId=" + getToId()
+ + " injectionPoint=" + injectionPoint + "}";
+ }
+
+ @Override public Edge copy(NodeId fromId, NodeId toId) {
+ return new DependencyEdge(fromId, toId, injectionPoint);
}
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/Edge.java b/extensions/grapher/src/com/google/inject/grapher/Edge.java
new file mode 100644
index 0000000..52a1314
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/Edge.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.base.Objects;
+
+/**
+ * Edge in a guice dependency graph.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public abstract class Edge {
+ private final NodeId fromId;
+ private final NodeId toId;
+
+ protected Edge(NodeId fromId, NodeId toId) {
+ this.fromId = fromId;
+ this.toId = toId;
+ }
+
+ public NodeId getFromId() {
+ return fromId;
+ }
+
+ public NodeId getToId() {
+ return toId;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof Edge)) {
+ return false;
+ }
+ Edge other = (Edge) obj;
+ return Objects.equal(fromId, other.fromId) && Objects.equal(toId, other.toId);
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(fromId, toId);
+ }
+
+ /**
+ * Returns a copy of the edge with new node IDs.
+ *
+ * @param fromId new ID of the 'from' node
+ * @param toId new ID of the 'to' node
+ * @return copy of the edge with the new node IDs
+ */
+ public abstract Edge copy(NodeId fromId, NodeId toId);
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/NodeAliasFactory.java b/extensions/grapher/src/com/google/inject/grapher/EdgeCreator.java
similarity index 60%
rename from extensions/grapher/src/com/google/inject/grapher/NodeAliasFactory.java
rename to extensions/grapher/src/com/google/inject/grapher/EdgeCreator.java
index 2e4fef1..3c015b6 100644
--- a/extensions/grapher/src/com/google/inject/grapher/NodeAliasFactory.java
+++ b/extensions/grapher/src/com/google/inject/grapher/EdgeCreator.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2008 Google Inc.
+ * Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,16 @@
package com.google.inject.grapher;
+import com.google.inject.Binding;
+
/**
- * Factory for aliasing one node ID to another. Used when we don't want to
- * render {@link Key}s from {@link Binding}s that are not interesting.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
+ * Creator of graph edges to render. All edges will be rendered on the graph after node aliasing is
+ * performed.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
*/
-public interface NodeAliasFactory<K> {
- /**
- * Makes edges that would point to {@code fromId} point to
- * {@code toId} instead.
- */
- void newAlias(K fromId, K toId);
+public interface EdgeCreator {
+
+ /** Returns edges for the given dependency graph. */
+ Iterable<Edge> getEdges(Iterable<Binding<?>> bindings);
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/GrapherModule.java b/extensions/grapher/src/com/google/inject/grapher/GrapherModule.java
deleted file mode 100644
index 2b945d2..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/GrapherModule.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Key;
-import com.google.inject.TypeLiteral;
-import com.google.inject.spi.BindingTargetVisitor;
-
-import java.util.Collection;
-
-/**
- * Module for the common bindings for {@link InjectorGrapher}. You will also
- * need to bind a {@link Module} that satisfies the {@link Renderer}
- * dependency.
- * <p>
- * If you want to use subtypes of the node and edge classes, or a different
- * node ID type, you will need to override the {@link GraphingVisitor} binding
- * to specify the new type parameters.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class GrapherModule extends AbstractModule {
- @Override
- protected void configure() {
- requireBinding(Renderer.class);
-
- bind(InjectorGrapher.class);
-
- bind(new TypeLiteral<BindingTargetVisitor<Object, Collection<Key<?>>>>() {})
- .to(TransitiveDependencyVisitor.class);
-
- bind(new TypeLiteral<BindingTargetVisitor<Object, Void>>() {})
- .to(new TypeLiteral<GraphingVisitor<String, InterfaceNode<String>,
- ImplementationNode<String>, BindingEdge<String>, DependencyEdge<String>>>() {});
- }
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/GraphingVisitor.java b/extensions/grapher/src/com/google/inject/grapher/GraphingVisitor.java
deleted file mode 100644
index 41a3be7..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/GraphingVisitor.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher;
-
-import com.google.common.collect.Lists;
-import com.google.inject.Binding;
-import com.google.inject.Inject;
-import com.google.inject.Key;
-import com.google.inject.Provider;
-import com.google.inject.spi.BindingTargetVisitor;
-import com.google.inject.spi.ConstructorBinding;
-import com.google.inject.spi.ConvertedConstantBinding;
-import com.google.inject.spi.Dependency;
-import com.google.inject.spi.ExposedBinding;
-import com.google.inject.spi.HasDependencies;
-import com.google.inject.spi.InjectionPoint;
-import com.google.inject.spi.InstanceBinding;
-import com.google.inject.spi.LinkedKeyBinding;
-import com.google.inject.spi.ProviderBinding;
-import com.google.inject.spi.ProviderInstanceBinding;
-import com.google.inject.spi.ProviderKeyBinding;
-import com.google.inject.spi.UntargettedBinding;
-
-import java.lang.reflect.Member;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * {@link BindingTargetVisitor} that adds nodes and edges to the graph based on
- * the visited {@link Binding}.
- * <p>
- * This class is parameterized over the four graph element types
- * ({@link InterfaceNode}, {@link ImplementationNode}, {@link BindingEdge}, and
- * {@link DependencyEdge}) so that you can extend those interfaces and also
- * extend this class, and the helper methods will all return your new types.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- *
- * @param <K> The type for node IDs.
- * @param <N> Type for {@link InterfaceNode}s.
- * @param <M> Type for {@link ImplementationNode}.s
- * @param <B> Type for {@link BindingEdge}s.
- * @param <D> Type for {@link DependencyEdge}s.
- */
-public class GraphingVisitor<K, N extends InterfaceNode<K>, M extends ImplementationNode<K>,
- B extends BindingEdge<K>, D extends DependencyEdge<K>>
-implements BindingTargetVisitor<Object, Void> {
-
- private final NodeIdFactory<K> idFactory;
-
- private final InterfaceNode.Factory<K, N> interfaceNodeFactory;
- private final ImplementationNode.Factory<K, M> implementationNodeFactory;
- private final BindingEdge.Factory<K, B> bindingEdgeFactory;
- private final DependencyEdge.Factory<K, D> dependencyEdgeFactory;
- private final NodeAliasFactory<K> nodeAliasFactory;
-
- @Inject
- public GraphingVisitor(NodeIdFactory<K> idFactory,
- InterfaceNode.Factory<K, N> interfaceNodeFactory,
- ImplementationNode.Factory<K, M> implementationNodeFactory,
- BindingEdge.Factory<K, B> bindingEdgeFactory,
- DependencyEdge.Factory<K, D> dependencyEdgeFactory,
- NodeAliasFactory<K> nodeAliasFactory) {
- this.idFactory = idFactory;
- this.interfaceNodeFactory = interfaceNodeFactory;
- this.implementationNodeFactory = implementationNodeFactory;
- this.bindingEdgeFactory = bindingEdgeFactory;
- this.dependencyEdgeFactory = dependencyEdgeFactory;
- this.nodeAliasFactory = nodeAliasFactory;
- }
-
- /**
- * Helper method to return the standard node ID for the {@link Binding}'s
- * {@link Key}.
- *
- * @see NodeIdFactory#getClassNodeId(Key)
- */
- protected final K getClassNodeId(Binding<?> binding) {
- return idFactory.getClassNodeId(binding.getKey());
- }
-
- /**
- * Helper method to return the instance node ID for the {@link Binding}'s
- * {@link Key}.
- *
- * @see NodeIdFactory#getInstanceNodeId(Key)
- */
- protected final K getInstanceNodeId(Binding<?> binding) {
- return idFactory.getInstanceNodeId(binding.getKey());
- }
-
- /**
- * Creates and returns a new {@link InterfaceNode} object for the given
- * {@link Binding}.
- */
- protected N newInterfaceNode(Binding<?> binding) {
- N node = interfaceNodeFactory.newInterfaceNode(getClassNodeId(binding));
- node.setKey(binding.getKey());
- node.setSource(binding.getSource());
-
- return node;
- }
-
- /**
- * Creates and returns a new {@link ImplementationNode} for the given
- * {@link Binding}, where the {@link Binding} is for a class that Guice
- * will instantiate, rather than a specific instance.
- */
- protected M newClassImplementationNode(Binding<?> binding,
- InjectionPoint constructorInjectionPoint,
- Collection<InjectionPoint> memberInjectionPoints) {
- M node = implementationNodeFactory.newImplementationNode(getClassNodeId(binding));
- node.setClassKey(binding.getKey());
- // we don't set the source here because it's not interesting for classes
-
- node.addMember(constructorInjectionPoint.getMember());
- for (InjectionPoint injectionPoint : memberInjectionPoints) {
- node.addMember(injectionPoint.getMember());
- }
-
- return node;
- }
-
- /**
- * Creates and returns a new {@link ImplementationNode} for the given
- * {@link Binding}, where the {@link Binding} is for an instance, rather than
- * a class.
- */
- protected M newInstanceImplementationNode(Binding<?> binding, Object instance) {
- M node = implementationNodeFactory.newImplementationNode(getInstanceNodeId(binding));
- node.setSource(binding.getSource());
- node.setInstance(instance);
-
- return node;
- }
-
- /**
- * Creates a new {@link BindingEdge} from the given node to the specified
- * node.
- *
- * @param nodeId ID of the {@link InterfaceNode} that binds to the other.
- * @param toId The node ID of a class or instance that is bound.
- * @param type The {@link BindingEdge.Type} of this binding.
- * @return The newly-created and added {@link BindingEdge}.
- */
- protected B newBindingEdge(K nodeId, K toId, BindingEdge.Type type) {
- B edge = bindingEdgeFactory.newBindingEdge(nodeId, toId);
- edge.setType(type);
-
- return edge;
- }
-
- /**
- * Adds {@link DependencyEdge}s to the graph for each of the provided
- * {@link Dependency}s. These will be from the given node ID to the
- * {@link Dependency}'s {@link Key}.
- * <p>
- * If a {@link Dependency} has an associated {@link InjectionPoint}, its
- * member will be added to the given {@link ImplementationNode} and the edge
- * will start at the {@link Member}.
- *
- * @see #newDependencyEdge(Object, InjectionPoint, Dependency)
- *
- * @param nodeId ID of the node that should be the tail of the
- * {@link DependencyEdge}s.
- * @param node An {@link ImplementationNode} to add {@link Member}s to.
- * @param dependencies {@link Collection} of {@link Dependency}s from the
- * {@link Binding}.
- * @return A {@link Collection} of the {@link DependencyEdge}s that were
- * added to the graph.
- */
- protected Collection<D> newDependencyEdges(K nodeId, M node,
- Collection<Dependency<?>> dependencies) {
- List<D> edges = Lists.newArrayList();
-
- for (Dependency<?> dependency : dependencies) {
- InjectionPoint injectionPoint = dependency.getInjectionPoint();
-
- if (injectionPoint != null) {
- node.addMember(injectionPoint.getMember());
- }
-
- D edge = newDependencyEdge(nodeId, injectionPoint, dependency);
- edges.add(edge);
- }
-
- return edges;
- }
-
- /**
- * Creates a new {@link DependencyEdge} from the given node to a
- * {@link Dependency}.
- * <p>
- * This method takes more comprehensive parameters than strictly necessary
- * in case they would be useful to overriding implementations.
- *
- * @param nodeId ID of the {@link ImplementationNode} where the edges will start.
- * @param injectionPoint The {@link InjectionPoint} that gave rise to this
- * {@link Dependency}, if one exists. Used to figure out which
- * {@link Member} the edge should point from.
- * @param dependency The {@link Dependency} to represent with this edge.
- * @return The newly-created and added {@link DependencyEdge}.
- */
- protected D newDependencyEdge(K nodeId,
- InjectionPoint injectionPoint, Dependency<?> dependency) {
- K toId = idFactory.getClassNodeId(dependency.getKey());
- return dependencyEdgeFactory.newDependencyEdge(nodeId, injectionPoint, toId);
- }
-
-
- /**
- * Visitor for {@link ConstructorBinding}s. These are for classes that Guice
- * will instantiate to satisfy injection requests. We create a new
- * {@link ImplementationNode} for the class, then add edges to everything
- * that it depends on to be instantiated.
- *
- * @see #newClassImplementationNode(Binding, InjectionPoint, Collection)
- * @see #newDependencyEdges(Object, ImplementationNode, Collection)
- */
- public Void visit(ConstructorBinding<?> binding) {
- M node = newClassImplementationNode(binding, binding.getConstructor(),
- binding.getInjectableMembers());
- newDependencyEdges(getClassNodeId(binding), node, binding.getDependencies());
-
- return null;
- }
-
- /**
- * Visitor for {@link ConvertedConstantBinding}. The {@link Binding}'s
- * {@link Key} will be of an annotated primitive type, and the value of
- * {@link ConvertedConstantBinding#getSourceKey()} will be of a
- * {@link String} with the same annotation.
- * <p>
- * We render this as an {@link InterfaceNode} that has a
- * {@link BindingEdge} to the source {@link Key}. That will then be rendered
- * by {@link #visit(InstanceBinding)} as an {@link InterfaceNode}
- * with a {@link BindingEdge} to the {@link String} instance.
- *
- * @see #newInterfaceNode(Binding)
- * @see #newBindingEdge(Object, Object, com.google.inject.grapher.BindingEdge.Type)
- */
- public Void visit(ConvertedConstantBinding<?> binding) {
- newInterfaceNode(binding);
- newBindingEdge(getClassNodeId(binding), idFactory.getClassNodeId(binding.getSourceKey()),
- BindingEdge.Type.CONVERTED_CONSTANT);
-
- return null;
- }
-
- /**
- * Currently not displayed on the graph.
- */
- public Void visit(ExposedBinding<?> binding) {
- // TODO(phopkins): Decide if this is needed for graphing.
- return null;
- }
-
- /**
- * Visitor for {@link InstanceBinding}. We render two nodes in this case: a
- * {@link InterfaceNode} for the binding's {@link Key}, and then an
- * {@link ImplementationNode} for the instance {@link Object} itself. We run
- * a binding node between them.
- * <p>
- * We then render any {@link DependencyEdge}s that the instance may have,
- * which come either from {@link InjectionPoint}s (method and field) on the
- * instance, or on {@link Dependency}s the instance declares through the
- * {@link HasDependencies} interface.
- *
- * @see #newInterfaceNode(Binding)
- * @see #newBindingEdge(Object, Object, com.google.inject.grapher.BindingEdge.Type)
- * @see #newInstanceImplementationNode(Binding, Object)
- * @see #newDependencyEdges(Object, ImplementationNode, java.util.Collection)
- */
- public Void visit(InstanceBinding<?> binding) {
- newInterfaceNode(binding);
- newBindingEdge(getClassNodeId(binding), getInstanceNodeId(binding),
- BindingEdge.Type.NORMAL);
-
- M node = newInstanceImplementationNode(binding, binding.getInstance());
- newDependencyEdges(getInstanceNodeId(binding), node, binding.getDependencies());
-
- return null;
- }
-
- /**
- * Visitor for {@link LinkedKeyBinding}. This is the standard {@link Binding}
- * you get from binding an interface class to an implementation class. We
- * create an {@link InterfaceNode}, then draw a {@link BindingEdge} to the
- * node of the implementing class.
- *
- * @see #newInterfaceNode(Binding)
- * @see #newBindingEdge(Object, Object, com.google.inject.grapher.BindingEdge.Type)
- */
- public Void visit(LinkedKeyBinding<?> binding) {
- newInterfaceNode(binding);
- newBindingEdge(getClassNodeId(binding), idFactory.getClassNodeId(binding.getLinkedKey()),
- BindingEdge.Type.NORMAL);
-
- return null;
- }
-
- /**
- * Visitor for {@link ProviderBinding}. These {@link Binding}s arise from an
- * {@link InjectionPoint} for the {@link Provider} interface. Since this
- * isn't tremendously interesting information, we don't render this binding
- * on the graph, and instead let the {@link DependencyEdge} go straight from
- * the {@link InjectionPoint} to the node specified by
- * {@link ProviderBinding#getProvidedKey()}.
- *
- * @see NodeAliasFactory#newAlias(Object, Object)
- */
- public Void visit(ProviderBinding<?> binding) {
- nodeAliasFactory.newAlias(getClassNodeId(binding),
- idFactory.getClassNodeId(binding.getProvidedKey()));
-
- return null;
- }
-
- /**
- * Same as {@link #visit(InstanceBinding)}, but the
- * {@link BindingEdge} is {@link BindingEdge.Type#PROVIDER}.
- *
- * @see #newInterfaceNode(Binding)
- * @see #newBindingEdge(Object, Object, com.google.inject.grapher.BindingEdge.Type)
- * @see #newInstanceImplementationNode(Binding, Object)
- * @see #newDependencyEdges(Object, ImplementationNode, java.util.Collection)
- */
- public Void visit(ProviderInstanceBinding<?> binding) {
- newInterfaceNode(binding);
- newBindingEdge(getClassNodeId(binding), getInstanceNodeId(binding), BindingEdge.Type.PROVIDER);
-
- M node = newInstanceImplementationNode(binding, binding.getProviderInstance());
- newDependencyEdges(getInstanceNodeId(binding), node, binding.getDependencies());
-
- return null;
- }
-
- /**
- * Same as {@link #visit(LinkedKeyBinding)}, but the
- * {@link BindingEdge} is {@link BindingEdge.Type#PROVIDER}.
- *
- * @see #newInterfaceNode(Binding)
- * @see #newBindingEdge(Object, Object, com.google.inject.grapher.BindingEdge.Type)
- */
- public Void visit(ProviderKeyBinding<?> binding) {
- newInterfaceNode(binding);
- newBindingEdge(getClassNodeId(binding), idFactory.getClassNodeId(binding.getProviderKey()),
- BindingEdge.Type.PROVIDER);
-
- return null;
- }
-
- /**
- * Currently not displayed on the graph.
- */
- public Void visit(UntargettedBinding<?> binding) {
- // TODO(phopkins): Decide if this is needed for graphing.
- return null;
- }
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/ImplementationNode.java b/extensions/grapher/src/com/google/inject/grapher/ImplementationNode.java
index 1cbd185..b4a67a0 100644
--- a/extensions/grapher/src/com/google/inject/grapher/ImplementationNode.java
+++ b/extensions/grapher/src/com/google/inject/grapher/ImplementationNode.java
@@ -16,52 +16,48 @@
package com.google.inject.grapher;
-import com.google.inject.Key;
-
+import com.google.common.base.Objects;
import java.lang.reflect.Member;
+import java.util.Collection;
/**
- * Node for classes and instances that have {@link Dependency}s and are
- * bound to {@link InterfaceNode}s. These nodes will often have fields for
- * {@link Member}s that are {@link InjectionPoint}s.
- *
+ * Node for types that have {@link Dependency}s and are bound to {@link InterfaceNode}s. These
+ * nodes will often have fields for {@link Member}s that are {@link InjectionPoint}s.
+ *
* @see DependencyEdge
*
* @author phopkins@gmail.com (Pete Hopkins)
- *
- * @param <K> The type for node IDs.
*/
-public interface ImplementationNode<K> {
- /**
- * Sets the {@link Key} that this node is for. Used when the node is
- * representing a class that Guice will instantiate.
- */
- void setClassKey(Key<?> key);
+public class ImplementationNode extends Node {
+ private final Collection<Member> members;
- /**
- * Sets the {@link Object} that's the already-created instance. Used when
- * this node is represeting the instance instead of a class.
- */
- void setInstance(Object instance);
+ public ImplementationNode(NodeId id, Object source, Collection<Member> members) {
+ super(id, source);
+ this.members = members;
+ }
- void setSource(Object source);
- void addMember(Member member);
+ public Collection<Member> getMembers() {
+ return members;
+ }
- /**
- * Factory interface for {@link ImplementationNode}s. Renderer
- * implementations will need to provide an implementation for this.
- *
- * @param <K> The type for node IDs.
- * @param <T> The {@link ImplementationNode} sub-type that this factory
- * provides.
- */
- interface Factory<K, T extends ImplementationNode<K>> {
- /**
- * Creates a new {@link ImplementationNode} and adds it to the graph.
- *
- * @param nodeId ID for the node.
- * @return The new {@link ImplementationNode} instance.
- */
- T newImplementationNode(K nodeId);
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof ImplementationNode)) {
+ return false;
+ }
+ ImplementationNode other = (ImplementationNode) obj;
+ return super.equals(other) && Objects.equal(members, other.members);
+ }
+
+ @Override public int hashCode() {
+ return 31 * super.hashCode() + Objects.hashCode(members);
+ }
+
+ @Override public String toString() {
+ return "ImplementationNode{id=" + getId() + " source=" + getSource()
+ + " members=" + members + "}";
+ }
+
+ @Override public Node copy(NodeId id) {
+ return new ImplementationNode(id, getSource(), getMembers());
}
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/InjectorGrapher.java b/extensions/grapher/src/com/google/inject/grapher/InjectorGrapher.java
index bb70fd6..91fd92c 100644
--- a/extensions/grapher/src/com/google/inject/grapher/InjectorGrapher.java
+++ b/extensions/grapher/src/com/google/inject/grapher/InjectorGrapher.java
@@ -16,151 +16,26 @@
package com.google.inject.grapher;
-import com.google.common.collect.Sets;
-import com.google.inject.Binding;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
-import com.google.inject.spi.BindingTargetVisitor;
import java.io.IOException;
-import java.util.Collection;
-import java.util.Iterator;
import java.util.Set;
-import java.util.logging.Logger;
/**
- * Root class for graphing an {@link Injector}. Bound in {@link GrapherModule}.
- * <p>
- * Use {@link #of(Injector)} to specify the {@link Injector} to use, and
- * {@link graph()} to graph the {@link Injector} using the currently-bound
- * {@link Renderer}.
- * <p>
- * By default, this will graph the entire {@link Injector}. Use
- * {@link #rootedAt(Class...)} or {@link #rootedAt(Key...)} to specify an
- * initial set of {@link Class}es or {@link Key}s to use, and this will graph
- * their transitive bindings and dependencies.
+ * Guice injector grapher. Renders the guice dependency graph for an injector. It can render the
+ * whole dependency graph or just transitive dependencies of a given set of nodes.
*
* @author phopkins@gmail.com (Pete Hopkins)
*/
-public class InjectorGrapher {
- private static final Key<Logger> loggerKey = Key.get(Logger.class);
+public interface InjectorGrapher {
- private final BindingTargetVisitor<Object, Collection<Key<?>>> keyVisitor;
- private final BindingTargetVisitor<Object, Void> graphingVisitor;
- private final Renderer renderer;
-
- private Injector injector;
- private Set<Key<?>> root;
-
- @Inject
- public InjectorGrapher(BindingTargetVisitor<Object, Collection<Key<?>>> keyVisitor,
- BindingTargetVisitor<Object, Void> graphingVisitor, Renderer renderer) {
- this.keyVisitor = keyVisitor;
- this.graphingVisitor = graphingVisitor;
- this.renderer = renderer;
- }
+ /** Graphs the guice dependency graph for the given injector using default starting keys. */
+ void graph(Injector injector) throws IOException;
/**
- * Sets the {@link Injector} to graph.
+ * Graphs the guice dependency graph for the given injector using the given starting keys and
+ * their transitive dependencies.
*/
- public InjectorGrapher of(Injector injector) {
- this.injector = injector;
- return this;
- }
-
- /**
- * Sets an initial group of {@link Class}es to use as the starting point for
- * the graph. The graph will be of these classes and their transitive
- * dependencies and bindings.
- */
- public InjectorGrapher rootedAt(Class<?>... classes) {
- this.root = Sets.newHashSet();
-
- for (Class<?> clazz : classes) {
- this.root.add(Key.get(clazz));
- }
-
- return this;
- }
-
- /**
- * Sets an initial group of {@link Key}s to use as the starting point for
- * the graph. The graph will be of these keys and their transitive
- * dependencies and bindings.
- */
- public InjectorGrapher rootedAt(Key<?>... keys) {
- this.root = Sets.newHashSet();
-
- for (Key<?> key : keys) {
- this.root.add(key);
- }
-
- return this;
- }
-
- /**
- * Renders a graph with the bound {@link Renderer}. The {@link Injector}
- * must have already been specified with {@link #of(Injector)}.
- */
- public void graph() throws IOException {
- processBindings();
- renderer.render();
- }
-
- /**
- * Tests for {@link Key}s that we don't want to include by default in the
- * graph. They're left out of the initial set, but will appear if depended
- * upon by other classes. Leaves out Guice classes (such as the
- * {@link Injector}) and the {@link Logger}.
- */
- private boolean skipKey(Key<?> key) {
- return key.getTypeLiteral().getRawType().getPackage() == Guice.class.getPackage()
- || loggerKey.equals(key);
- }
-
- /**
- * Takes the set of starting {@link Binding}s, which comes either from the
- * {@link Injector} or from {@link #rootedAt(Class...)}, and applies the
- * {@link #graphingVisitor} to them. Uses the {@link #keyVisitor} to build
- * out the set of {@link Key}s so that the graph covers the transitive
- * dependencies and bindings.
- */
- private void processBindings() {
- Set<Key<?>> keys = Sets.newLinkedHashSet();
- Set<Key<?>> visitedKeys = Sets.newHashSet();
-
- // build up the root set from the Injector if it wasn't specified
- if (root == null) {
- for (Key<?> key : injector.getBindings().keySet()) {
- if (!skipKey(key)) {
- keys.add(key);
- }
- }
- } else {
- keys.addAll(root);
- }
-
- while (!keys.isEmpty()) {
- Iterator<Key<?>> iterator = keys.iterator();
- Key<?> key = iterator.next();
- iterator.remove();
-
- if (visitedKeys.contains(key)) {
- continue;
- }
-
- Binding<?> binding = injector.getBinding(key);
- visitedKeys.add(key);
-
- binding.acceptTargetVisitor(graphingVisitor);
-
- // find the dependencies and make sure that they get visited
- Collection<Key<?>> newKeys = binding.acceptTargetVisitor(keyVisitor);
- if (newKeys != null) {
- keys.addAll(newKeys);
- }
- }
- }
+ void graph(Injector injector, Set<Key<?>> root) throws IOException;
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/InstanceNode.java b/extensions/grapher/src/com/google/inject/grapher/InstanceNode.java
new file mode 100644
index 0000000..cda56bc
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/InstanceNode.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.base.Objects;
+import java.lang.reflect.Member;
+
+/**
+ * Node for instances. Used when a type is bound to an instance.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public class InstanceNode extends Node {
+ private final Object instance;
+ private final Iterable<Member> members;
+
+ public InstanceNode(NodeId id, Object source, Object instance, Iterable<Member> members) {
+ super(id, source);
+ this.instance = instance;
+ this.members = members;
+ }
+
+ public Object getInstance() {
+ return instance;
+ }
+
+ public Iterable<Member> getMembers() {
+ return members;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof InstanceNode)) {
+ return false;
+ }
+ InstanceNode other = (InstanceNode) obj;
+ return super.equals(other) && Objects.equal(instance, other.instance)
+ && Objects.equal(members, other.members);
+ }
+
+ @Override public int hashCode() {
+ return 31 * super.hashCode() + Objects.hashCode(instance, members);
+ }
+
+ @Override public String toString() {
+ return "InstanceNode{id=" + getId() + " source=" + getSource() + " instance=" + instance
+ + " members=" + members + "}";
+ }
+
+ @Override public Node copy(NodeId id) {
+ return new InstanceNode(id, getSource(), getInstance(), getMembers());
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/InterfaceNode.java b/extensions/grapher/src/com/google/inject/grapher/InterfaceNode.java
index 0e343e5..58ae2f0 100644
--- a/extensions/grapher/src/com/google/inject/grapher/InterfaceNode.java
+++ b/extensions/grapher/src/com/google/inject/grapher/InterfaceNode.java
@@ -16,36 +16,27 @@
package com.google.inject.grapher;
-import com.google.inject.Key;
-
/**
- * Node for an interface class that has been bound to an implementation class
- * or instance. These nodes are basically defined by a {@link Key}.
+ * Node for an interface type that has been bound to an implementation class or instance.
*
* @see BindingEdge
*
* @author phopkins@gmail.com (Pete Hopkins)
- *
- * @param <K> The type for node IDs.
*/
-public interface InterfaceNode<K> {
- void setKey(Key<?> key);
- void setSource(Object source);
+public class InterfaceNode extends Node {
+ public InterfaceNode(NodeId id, Object source) {
+ super(id, source);
+ }
- /**
- * Factory interface for {@link InterfaceNode}s. Renderer implementations
- * will need to provide an implementation for this.
- *
- * @param <K> The type for node IDs.
- * @param <T> The {@link InterfaceNode} sub-type that this factory provides.
- */
- interface Factory<K, T extends InterfaceNode<K>> {
- /**
- * Creates a new {@link InterfaceNode} and adds it to the graph.
- *
- * @param nodeId ID for the node.
- * @return The new {@link InterfaceNode} instance.
- */
- T newInterfaceNode(K nodeId);
+ @Override public Node copy(NodeId id) {
+ return new InterfaceNode(id, getSource());
+ }
+
+ @Override public boolean equals(Object obj) {
+ return (obj instanceof InterfaceNode) && super.equals(obj);
+ }
+
+ @Override public String toString() {
+ return "InterfaceNode{id=" + getId() + " source=" + getSource() + "}";
}
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/Node.java b/extensions/grapher/src/com/google/inject/grapher/Node.java
new file mode 100644
index 0000000..870f805
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/Node.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.base.Objects;
+
+/**
+ * Node in a guice dependency graph.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public abstract class Node {
+ /**
+ * When set to true, the source object is ignored in {@link #equals} and {@link #hashCode}.
+ * Only used in tests.
+ */
+ static boolean ignoreSourceInComparisons = false;
+
+ private final NodeId id;
+ private final Object source;
+
+ protected Node(NodeId id, Object source) {
+ this.id = id;
+ this.source = source;
+ }
+
+ public NodeId getId() {
+ return id;
+ }
+
+ public Object getSource() {
+ return source;
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof Node)) {
+ return false;
+ }
+ Node other = (Node) obj;
+ return Objects.equal(id, other.id)
+ && (ignoreSourceInComparisons || Objects.equal(source, other.source));
+ }
+
+ @Override public int hashCode() {
+ return ignoreSourceInComparisons ? id.hashCode() : Objects.hashCode(id, source);
+ }
+
+ /**
+ * Returns a copy of the node with a new ID.
+ *
+ * @param id new ID of the node
+ * @return copy of the node with a new ID
+ */
+ public abstract Node copy(NodeId id);
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/NodeAliasFactory.java b/extensions/grapher/src/com/google/inject/grapher/NodeCreator.java
similarity index 60%
copy from extensions/grapher/src/com/google/inject/grapher/NodeAliasFactory.java
copy to extensions/grapher/src/com/google/inject/grapher/NodeCreator.java
index 2e4fef1..e22688f 100644
--- a/extensions/grapher/src/com/google/inject/grapher/NodeAliasFactory.java
+++ b/extensions/grapher/src/com/google/inject/grapher/NodeCreator.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2008 Google Inc.
+ * Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,15 @@
package com.google.inject.grapher;
+import com.google.inject.Binding;
+
/**
- * Factory for aliasing one node ID to another. Used when we don't want to
- * render {@link Key}s from {@link Binding}s that are not interesting.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
+ * Creator of graph nodes.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
*/
-public interface NodeAliasFactory<K> {
- /**
- * Makes edges that would point to {@code fromId} point to
- * {@code toId} instead.
- */
- void newAlias(K fromId, K toId);
+public interface NodeCreator {
+
+ /** Returns nodes for the given dependency graph. */
+ Iterable<Node> getNodes(Iterable<Binding<?>> bindings);
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/NodeId.java b/extensions/grapher/src/com/google/inject/grapher/NodeId.java
new file mode 100644
index 0000000..eaf3f15
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/NodeId.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.base.Objects;
+import com.google.inject.Key;
+
+/**
+ * ID of a node in the graph. An ID is given by a {@link Key} and a node type, which is used to
+ * distinguish instances and implementation classes for the same key. For example
+ * {@code bind(Integer.class).toInstance(42)} produces two nodes: an
+ * interface node with the key of {@code Key<Integer>} and an instance node with the same
+ * {@link Key} and value of 42.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public final class NodeId {
+
+ /** Type of node. */
+ public enum NodeType {
+ /** Type or class node. */
+ TYPE,
+
+ /** Instance node, used when something is bound to an instance. */
+ INSTANCE
+ }
+
+ private final Key<?> key;
+ private final NodeType nodeType;
+
+ private NodeId(Key<?> key, NodeType nodeType) {
+ this.key = key;
+ this.nodeType = nodeType;
+ }
+
+ public static NodeId newTypeId(Key<?> key) {
+ return new NodeId(key, NodeType.TYPE);
+ }
+
+ public static NodeId newInstanceId(Key<?> key) {
+ return new NodeId(key, NodeType.INSTANCE);
+ }
+
+ public Key<?> getKey() {
+ return key;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hashCode(key, nodeType);
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj.getClass().equals(NodeId.class))) {
+ return false;
+ }
+ NodeId other = (NodeId) obj;
+ return Objects.equal(key, other.key) && Objects.equal(nodeType, other.nodeType);
+ }
+
+ @Override public String toString() {
+ return "NodeId{nodeType=" + nodeType + " key=" + key + "}";
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/NodeIdFactory.java b/extensions/grapher/src/com/google/inject/grapher/NodeIdFactory.java
deleted file mode 100644
index a61e24b..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/NodeIdFactory.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher;
-
-import com.google.inject.Key;
-
-/**
- * Factory for abstract identifiers for elements on the graph. Most graph nodes
- * will correspond directly to {@link Key}s, but we do this for additional
- * flexibility and because instances do not have separate {@link Key}s from the
- * interfaces they are bound to.
- * <p>
- * Node IDs are treated as opaque values by {@link GraphingVisitor} and the
- * other classes in this package.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- *
- * @param <K> The type for node IDs.
- */
-public interface NodeIdFactory<K> {
- K getClassNodeId(Key<?> key);
- K getInstanceNodeId(Key<?> key);
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/ProviderAliasCreator.java b/extensions/grapher/src/com/google/inject/grapher/ProviderAliasCreator.java
new file mode 100644
index 0000000..d55ea7f
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/ProviderAliasCreator.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.collect.Lists;
+import com.google.inject.Binding;
+import com.google.inject.spi.ProviderBinding;
+import java.util.List;
+
+/**
+ * Alias creator that creates an alias for each {@link ProviderBinding}. These {@link Binding}s
+ * arise from an {@link InjectionPoint} for the {@link Provider} interface. Since this isn't
+ * very interesting information, we don't render this binding on the graph, and just alias the two
+ * nodes.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+final class ProviderAliasCreator implements AliasCreator {
+ @Override public Iterable<Alias> createAliases(Iterable<Binding<?>> bindings) {
+ List<Alias> aliases = Lists.newArrayList();
+ for (Binding<?> binding : bindings) {
+ if (binding instanceof ProviderBinding) {
+ aliases.add(new Alias(NodeId.newTypeId(binding.getKey()),
+ NodeId.newTypeId(((ProviderBinding<?>) binding).getProvidedKey())));
+ }
+ }
+ return aliases;
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/Renderer.java b/extensions/grapher/src/com/google/inject/grapher/Renderer.java
deleted file mode 100644
index a5e6b77..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/Renderer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher;
-
-import java.io.IOException;
-
-/**
- * Interface for the service that renders the graph. It is assumed that the
- * implementations for {@link BindingEdge.Factory},
- * {@link ImplementationNode.Factory}, etc. will have direct access to the
- * internals of the {@link Renderer} implementation, so the only external
- * interface needed is the {@link #render()} call that {@link InjectorGrapher}
- * calls when it's done.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public interface Renderer {
- void render() throws IOException;
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/RootKeySetCreator.java b/extensions/grapher/src/com/google/inject/grapher/RootKeySetCreator.java
new file mode 100644
index 0000000..f9e37d3
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/RootKeySetCreator.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import java.util.Set;
+
+/**
+ * Creator of the default starting set of keys to graph. These keys and their transitive
+ * dependencies will be graphed.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public interface RootKeySetCreator {
+
+ /** Returns the set of starting keys to graph. */
+ Set<Key<?>> getRootKeys(Injector injector);
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/StringNodeIdFactory.java b/extensions/grapher/src/com/google/inject/grapher/StringNodeIdFactory.java
deleted file mode 100644
index 2b2a115..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/StringNodeIdFactory.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher;
-
-import com.google.inject.Key;
-
-/**
- * {@link IdFactory} implementation that for {@link String} node IDs. The IDs
- * are comprised of letters, numbers and underscores.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class StringNodeIdFactory implements NodeIdFactory<String> {
- public String getClassNodeId(Key<?> key) {
- return "k_" + Integer.toHexString(key.hashCode());
- }
-
- public String getInstanceNodeId(Key<?> key) {
- return "i_" + Integer.toHexString(key.hashCode());
- }
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/TransitiveDependencyVisitor.java b/extensions/grapher/src/com/google/inject/grapher/TransitiveDependencyVisitor.java
index 27deaa6..51ba1cc 100644
--- a/extensions/grapher/src/com/google/inject/grapher/TransitiveDependencyVisitor.java
+++ b/extensions/grapher/src/com/google/inject/grapher/TransitiveDependencyVisitor.java
@@ -18,19 +18,18 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.inject.Binding;
import com.google.inject.Key;
-import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.ConvertedConstantBinding;
+import com.google.inject.spi.DefaultBindingTargetVisitor;
import com.google.inject.spi.Dependency;
-import com.google.inject.spi.ExposedBinding;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.LinkedKeyBinding;
import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderKeyBinding;
-import com.google.inject.spi.UntargettedBinding;
import java.util.Collection;
import java.util.Set;
@@ -38,13 +37,13 @@
/**
* {@link BindingTargetVisitor} that returns a {@link Collection} of the
* {@link Key}s of each {@link Binding}'s dependencies. Used by
- * {@link InjectorGropher} to walk the dependency graph from a starting set of
+ * {@link InjectorGrapher} to walk the dependency graph from a starting set of
* {@link Binding}s.
*
* @author phopkins@gmail.com (Pete Hopkins)
*/
public class TransitiveDependencyVisitor
-implements BindingTargetVisitor<Object, Collection<Key<?>>> {
+extends DefaultBindingTargetVisitor<Object, Collection<Key<?>>> {
private Collection<Key<?>> visitHasDependencies(HasDependencies hasDependencies) {
Set<Key<?>> dependencies = Sets.newHashSet();
@@ -56,41 +55,35 @@
return dependencies;
}
- public Collection<Key<?>> visit(ConstructorBinding<?> binding) {
+ @Override public Collection<Key<?>> visit(ConstructorBinding<?> binding) {
return visitHasDependencies(binding);
}
- public Collection<Key<?>> visit(ConvertedConstantBinding<?> binding) {
+ @Override public Collection<Key<?>> visit(ConvertedConstantBinding<?> binding) {
return visitHasDependencies(binding);
}
- public Collection<Key<?>> visit(ExposedBinding<?> binding) {
- // TODO(phopkins): Figure out if this is needed for graphing.
- return ImmutableSet.of();
- }
-
- public Collection<Key<?>> visit(InstanceBinding<?> binding) {
+ @Override public Collection<Key<?>> visit(InstanceBinding<?> binding) {
return visitHasDependencies(binding);
}
- public Collection<Key<?>> visit(LinkedKeyBinding<?> binding) {
+ @Override public Collection<Key<?>> visit(LinkedKeyBinding<?> binding) {
return ImmutableSet.<Key<?>>of(binding.getLinkedKey());
}
- public Collection<Key<?>> visit(ProviderBinding<?> binding) {
+ @Override public Collection<Key<?>> visit(ProviderBinding<?> binding) {
return ImmutableSet.<Key<?>>of(binding.getProvidedKey());
}
- public Collection<Key<?>> visit(ProviderInstanceBinding<?> binding) {
+ @Override public Collection<Key<?>> visit(ProviderInstanceBinding<?> binding) {
return visitHasDependencies(binding);
}
- public Collection<Key<?>> visit(ProviderKeyBinding<?> binding) {
+ @Override public Collection<Key<?>> visit(ProviderKeyBinding<?> binding) {
return ImmutableSet.<Key<?>>of(binding.getProviderKey());
}
- public Collection<Key<?>> visit(UntargettedBinding<?> binding) {
- // TODO(phopkins): Figure out if this is needed for graphing.
- return null;
+ @Override public Collection<Key<?>> visitOther(Binding<?> binding) {
+ return ImmutableSet.of();
}
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/BindingEdgeFactory.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/BindingEdgeFactory.java
deleted file mode 100644
index 825f3ff..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/BindingEdgeFactory.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher.graphviz;
-
-import com.google.common.collect.ImmutableList;
-import com.google.inject.Inject;
-import com.google.inject.grapher.BindingEdge;
-
-/**
- * Graphviz-specific implementation of {@link BindingEdge.Factory}. Uses a
- * {@link GraphvizEdgeAdaptor} to delegate to a {@link GraphvizEdge}.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class BindingEdgeFactory implements BindingEdge.Factory<String, BindingEdge<String>> {
- private final GraphvizRenderer renderer;
-
- @Inject
- public BindingEdgeFactory(GraphvizRenderer renderer) {
- this.renderer = renderer;
- }
-
- public BindingEdge<String> newBindingEdge(String fromId, String toId) {
- GraphvizEdge edge = new GraphvizEdge(fromId, toId);
-
- renderer.addEdge(edge);
- return newAdaptor(edge);
- }
-
- protected GraphvizEdgeAdaptor newAdaptor(GraphvizEdge edge) {
- return new GraphvizEdgeAdaptor(edge);
- }
-
- /**
- * Adaptor class that converts {@link BindingEdge} methods to display
- * operations on a {@link GraphvizEdge}.
- */
- protected class GraphvizEdgeAdaptor implements BindingEdge<String> {
- protected final GraphvizEdge edge;
-
- public GraphvizEdgeAdaptor(GraphvizEdge edge) {
- this.edge = edge;
-
- this.edge.setStyle(EdgeStyle.DASHED);
- }
-
- public void setType(BindingEdge.Type type) {
- switch (type) {
- case NORMAL:
- edge.setArrowHead(ImmutableList.of(ArrowType.NORMAL_OPEN));
- break;
-
- case PROVIDER:
- edge.setArrowHead(ImmutableList.of(ArrowType.NORMAL_OPEN, ArrowType.NORMAL_OPEN));
- break;
-
- case CONVERTED_CONSTANT:
- edge.setArrowHead(ImmutableList.of(ArrowType.NORMAL_OPEN, ArrowType.DOT_OPEN));
- break;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/DependencyEdgeFactory.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/DependencyEdgeFactory.java
deleted file mode 100644
index ae9e441..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/DependencyEdgeFactory.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher.graphviz;
-
-import com.google.common.collect.ImmutableList;
-import com.google.inject.Inject;
-import com.google.inject.grapher.DependencyEdge;
-import com.google.inject.spi.InjectionPoint;
-
-/**
- * Graphviz-specific implementation of {@link DependencyEdge.Factory}. Uses a
- * {@link GraphvizEdgeAdaptor} to delegate to a {@link GraphvizEdge}.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class DependencyEdgeFactory
-implements DependencyEdge.Factory<String, DependencyEdge<String>> {
- private final GraphvizRenderer renderer;
- private final PortIdFactory portIdFactory;
-
- @Inject
- public DependencyEdgeFactory(GraphvizRenderer renderer, PortIdFactory portIdFactory) {
- this.renderer = renderer;
- this.portIdFactory = portIdFactory;
- }
-
- public DependencyEdge<String> newDependencyEdge(String fromId,
- InjectionPoint fromPoint, String toId) {
- GraphvizEdge edge = new GraphvizEdge(fromId, toId);
-
- if (fromPoint == null) {
- edge.setTailPortId("header");
- } else {
- edge.setTailPortId(portIdFactory.getPortId(fromPoint.getMember()));
- }
-
- renderer.addEdge(edge);
- return newAdaptor(edge);
- }
-
- protected GraphvizEdgeAdaptor newAdaptor(GraphvizEdge edge) {
- return new GraphvizEdgeAdaptor(edge);
- }
-
- /**
- * Adaptor class that converts {@link DependencyEdge} methods to display
- * operations on a {@link GraphvizEdge}.
- */
- protected class GraphvizEdgeAdaptor implements DependencyEdge<String> {
- protected final GraphvizEdge edge;
-
- public GraphvizEdgeAdaptor(GraphvizEdge edge) {
- this.edge = edge;
-
- this.edge.setArrowHead(ImmutableList.of(ArrowType.NORMAL));
- this.edge.setTailCompassPoint(CompassPoint.EAST);
- }
- }
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/Graphviz.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/Graphviz.java
new file mode 100644
index 0000000..b86eadf
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/graphviz/Graphviz.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher.graphviz;
+
+import com.google.inject.BindingAnnotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Annotation for types used by the graphviz grapher.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+@BindingAnnotation
+@Retention(RetentionPolicy.RUNTIME)
+@interface Graphviz {}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizEdge.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizEdge.java
index 9d1b9f0..39fd4bd 100644
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizEdge.java
+++ b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizEdge.java
@@ -17,6 +17,7 @@
package com.google.inject.grapher.graphviz;
import com.google.common.collect.ImmutableList;
+import com.google.inject.grapher.NodeId;
import java.util.List;
@@ -27,24 +28,24 @@
* @author phopkins@gmail.com (Pete Hopkins)
*/
public class GraphvizEdge {
- private final String headNodeId;
+ private final NodeId headNodeId;
private String headPortId;
private CompassPoint headCompassPoint;
private List<ArrowType> arrowHead = ImmutableList.of(ArrowType.NORMAL);
- private final String tailNodeId;
+ private final NodeId tailNodeId;
private String tailPortId;
private CompassPoint tailCompassPoint;
private List<ArrowType> arrowTail = ImmutableList.of(ArrowType.NONE);
private EdgeStyle style = EdgeStyle.SOLID;
- public GraphvizEdge(String tailNodeId, String headNodeId) {
+ public GraphvizEdge(NodeId tailNodeId, NodeId headNodeId) {
this.tailNodeId = tailNodeId;
this.headNodeId = headNodeId;
}
- public String getHeadNodeId() {
+ public NodeId getHeadNodeId() {
return headNodeId;
}
@@ -72,7 +73,7 @@
this.arrowHead = ImmutableList.copyOf(arrowHead);
}
- public String getTailNodeId() {
+ public NodeId getTailNodeId() {
return tailNodeId;
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java
new file mode 100644
index 0000000..c9daaea
--- /dev/null
+++ b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizGrapher.java
@@ -0,0 +1,325 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 com.google.inject.grapher.graphviz;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.grapher.AbstractInjectorGrapher;
+import com.google.inject.grapher.BindingEdge;
+import com.google.inject.grapher.DependencyEdge;
+import com.google.inject.grapher.ImplementationNode;
+import com.google.inject.grapher.InstanceNode;
+import com.google.inject.grapher.InterfaceNode;
+import com.google.inject.grapher.NameFactory;
+import com.google.inject.grapher.NodeId;
+import com.google.inject.spi.InjectionPoint;
+import java.io.PrintWriter;
+import java.lang.reflect.Member;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * {@link InjectorGrapher} implementation that writes out a Graphviz DOT file of the graph.
+ * Dependencies are bound in {@link GraphvizModule}.
+ * <p>
+ * Specify the {@link PrintWriter} to output to with {@link #setOut(PrintWriter)}.
+ *
+ * @author phopkins@gmail.com (Pete Hopkins)
+ */
+public class GraphvizGrapher extends AbstractInjectorGrapher {
+ private final Map<NodeId, GraphvizNode> nodes = Maps.newHashMap();
+ private final List<GraphvizEdge> edges = Lists.newArrayList();
+ private final NameFactory nameFactory;
+ private final PortIdFactory portIdFactory;
+
+ private PrintWriter out;
+ private String rankdir = "TB";
+
+ @Inject GraphvizGrapher(@Graphviz NameFactory nameFactory,
+ @Graphviz PortIdFactory portIdFactory) {
+ this.nameFactory = nameFactory;
+ this.portIdFactory = portIdFactory;
+ }
+
+ @Override protected void reset() {
+ nodes.clear();
+ edges.clear();
+ }
+
+ public void setOut(PrintWriter out) {
+ this.out = out;
+ }
+
+ public void setRankdir(String rankdir) {
+ this.rankdir = rankdir;
+ }
+
+ @Override protected void postProcess() {
+ start();
+
+ for (GraphvizNode node : nodes.values()) {
+ renderNode(node);
+ }
+
+ for (GraphvizEdge edge : edges) {
+ renderEdge(edge);
+ }
+
+ finish();
+
+ out.flush();
+ }
+
+ protected Map<String, String> getGraphAttributes() {
+ Map<String, String> attrs = Maps.newHashMap();
+ attrs.put("rankdir", rankdir);
+ return attrs;
+ }
+
+ protected void start() {
+ out.println("digraph injector {");
+
+ Map<String, String> attrs = getGraphAttributes();
+ out.println("graph " + getAttrString(attrs) + ";");
+ }
+
+ protected void finish() {
+ out.println("}");
+ }
+
+ protected void renderNode(GraphvizNode node) {
+ Map<String, String> attrs = getNodeAttributes(node);
+ out.println(node.getIdentifier() + " " + getAttrString(attrs));
+ }
+
+ protected Map<String, String> getNodeAttributes(GraphvizNode node) {
+ Map<String, String> attrs = Maps.newHashMap();
+
+ attrs.put("label", getNodeLabel(node));
+ // remove most of the margin because the table has internal padding
+ attrs.put("margin", "0.02,0");
+ attrs.put("shape", node.getShape().toString());
+ attrs.put("style", node.getStyle().toString());
+
+ return attrs;
+ }
+
+ /**
+ * Creates the "label" for a node. This is a string of HTML that defines a
+ * table with a heading at the top and (in the case of
+ * {@link ImplementationNode}s) rows for each of the member fields.
+ */
+ protected String getNodeLabel(GraphvizNode node) {
+ String cellborder = node.getStyle() == NodeStyle.INVISIBLE ? "1" : "0";
+
+ StringBuilder html = new StringBuilder();
+ html.append("<");
+ html.append("<table cellspacing=\"0\" cellpadding=\"5\" cellborder=\"");
+ html.append(cellborder).append("\" border=\"0\">");
+
+ html.append("<tr>").append("<td align=\"left\" port=\"header\" ");
+ html.append("bgcolor=\"" + node.getHeaderBackgroundColor() + "\">");
+
+ String subtitle = Joiner.on("<br align=\"left\"/>").join(node.getSubtitles());
+ if (subtitle.length() != 0) {
+ html.append("<font color=\"").append(node.getHeaderTextColor());
+ html.append("\" point-size=\"10\">");
+ html.append(subtitle).append("<br align=\"left\"/>").append("</font>");
+ }
+
+ html.append("<font color=\"" + node.getHeaderTextColor() + "\">");
+ html.append(htmlEscape(node.getTitle())).append("<br align=\"left\"/>");
+ html.append("</font>").append("</td>").append("</tr>");
+
+ for (Map.Entry<String, String> field : node.getFields().entrySet()) {
+ html.append("<tr>");
+ html.append("<td align=\"left\" port=\"").append(htmlEscape(field.getKey())).append("\">");
+ html.append(htmlEscape(field.getValue()));
+ html.append("</td>").append("</tr>");
+ }
+
+ html.append("</table>");
+ html.append(">");
+ return html.toString();
+ }
+
+ protected void renderEdge(GraphvizEdge edge) {
+ Map<String, String> attrs = getEdgeAttributes(edge);
+
+ String tailId = getEdgeEndPoint(nodes.get(edge.getTailNodeId()).getIdentifier(),
+ edge.getTailPortId(), edge.getTailCompassPoint());
+
+ String headId = getEdgeEndPoint(nodes.get(edge.getHeadNodeId()).getIdentifier(),
+ edge.getHeadPortId(), edge.getHeadCompassPoint());
+
+ out.println(tailId + " -> " + headId + " " + getAttrString(attrs));
+ }
+
+ protected Map<String, String> getEdgeAttributes(GraphvizEdge edge) {
+ Map<String, String> attrs = Maps.newHashMap();
+
+ attrs.put("arrowhead", getArrowString(edge.getArrowHead()));
+ attrs.put("arrowtail", getArrowString(edge.getArrowTail()));
+ attrs.put("style", edge.getStyle().toString());
+
+ return attrs;
+ }
+
+ private String getAttrString(Map<String, String> attrs) {
+ List<String> attrList = Lists.newArrayList();
+
+ for (Entry<String, String> attr : attrs.entrySet()) {
+ String value = attr.getValue();
+
+ if (value != null) {
+ attrList.add(attr.getKey() + "=" + value);
+ }
+ }
+
+ return "[" + Joiner.on(", ").join(attrList) + "]";
+ }
+
+ /**
+ * Turns a {@link List} of {@link ArrowType}s into a {@link String} that
+ * represents combining them. With Graphviz, that just means concatenating
+ * them.
+ */
+ protected String getArrowString(List<ArrowType> arrows) {
+ return Joiner.on("").join(arrows);
+ }
+
+ protected String getEdgeEndPoint(String nodeId, String portId, CompassPoint compassPoint) {
+ List<String> portStrings = Lists.newArrayList(nodeId);
+
+ if (portId != null) {
+ portStrings.add(portId);
+ }
+
+ if (compassPoint != null) {
+ portStrings.add(compassPoint.toString());
+ }
+
+ return Joiner.on(":").join(portStrings);
+ }
+
+ protected String htmlEscape(String str) {
+ return str.replace("&", "&").replace("<", "<").replace(">", ">");
+ }
+
+ protected List<String> htmlEscape(List<String> elements) {
+ List<String> escaped = Lists.newArrayList();
+ for (String element : elements) {
+ escaped.add(htmlEscape(element));
+ }
+ return escaped;
+ }
+
+ @Override protected void newInterfaceNode(InterfaceNode node) {
+ // TODO(phopkins): Show the Module on the graph, which comes from the
+ // class name when source is a StackTraceElement.
+
+ NodeId nodeId = node.getId();
+ GraphvizNode gnode = new GraphvizNode(nodeId);
+ gnode.setStyle(NodeStyle.DASHED);
+ Key<?> key = nodeId.getKey();
+ gnode.setTitle(nameFactory.getClassName(key));
+ gnode.addSubtitle(0, nameFactory.getAnnotationName(key));
+ addNode(gnode);
+ }
+
+ @Override protected void newImplementationNode(ImplementationNode node) {
+ NodeId nodeId = node.getId();
+ GraphvizNode gnode = new GraphvizNode(nodeId);
+ gnode.setStyle(NodeStyle.INVISIBLE);
+
+ gnode.setHeaderBackgroundColor("#000000");
+ gnode.setHeaderTextColor("#ffffff");
+ gnode.setTitle(nameFactory.getClassName(nodeId.getKey()));
+
+ for (Member member : node.getMembers()) {
+ gnode.addField(portIdFactory.getPortId(member), nameFactory.getMemberName(member));
+ }
+
+ addNode(gnode);
+ }
+
+ @Override protected void newInstanceNode(InstanceNode node) {
+ NodeId nodeId = node.getId();
+ GraphvizNode gnode = new GraphvizNode(nodeId);
+ gnode.setStyle(NodeStyle.INVISIBLE);
+
+ gnode.setHeaderBackgroundColor("#000000");
+ gnode.setHeaderTextColor("#ffffff");
+ gnode.setTitle(nameFactory.getClassName(nodeId.getKey()));
+
+ gnode.addSubtitle(0, nameFactory.getSourceName(node.getSource()));
+
+ gnode.setHeaderBackgroundColor("#aaaaaa");
+ gnode.setHeaderTextColor("#ffffff");
+ gnode.setTitle(nameFactory.getInstanceName(node.getInstance()));
+
+ for (Member member : node.getMembers()) {
+ gnode.addField(portIdFactory.getPortId(member), nameFactory.getMemberName(member));
+ }
+
+ addNode(gnode);
+ }
+
+ @Override protected void newDependencyEdge(DependencyEdge edge) {
+ GraphvizEdge gedge = new GraphvizEdge(edge.getFromId(), edge.getToId());
+ InjectionPoint fromPoint = edge.getInjectionPoint();
+ if (fromPoint == null) {
+ gedge.setTailPortId("header");
+ } else {
+ gedge.setTailPortId(portIdFactory.getPortId(fromPoint.getMember()));
+ }
+ gedge.setArrowHead(ImmutableList.of(ArrowType.NORMAL));
+ gedge.setTailCompassPoint(CompassPoint.EAST);
+
+ edges.add(gedge);
+ }
+
+ @Override protected void newBindingEdge(BindingEdge edge) {
+ GraphvizEdge gedge = new GraphvizEdge(edge.getFromId(), edge.getToId());
+ gedge.setStyle(EdgeStyle.DASHED);
+ switch (edge.getType()) {
+ case NORMAL:
+ gedge.setArrowHead(ImmutableList.of(ArrowType.NORMAL_OPEN));
+ break;
+
+ case PROVIDER:
+ gedge.setArrowHead(ImmutableList.of(ArrowType.NORMAL_OPEN, ArrowType.NORMAL_OPEN));
+ break;
+
+ case CONVERTED_CONSTANT:
+ gedge.setArrowHead(ImmutableList.of(ArrowType.NORMAL_OPEN, ArrowType.DOT_OPEN));
+ break;
+ }
+
+ edges.add(gedge);
+ }
+
+ private void addNode(GraphvizNode node) {
+ node.setIdentifier("x" + nodes.size());
+ nodes.put(node.getNodeId(), node);
+ }
+}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizModule.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizModule.java
index 9ae6ffe..db7f3f3 100644
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizModule.java
+++ b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizModule.java
@@ -17,43 +17,21 @@
package com.google.inject.grapher.graphviz;
import com.google.inject.AbstractModule;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.grapher.BindingEdge;
-import com.google.inject.grapher.DependencyEdge;
-import com.google.inject.grapher.ImplementationNode;
-import com.google.inject.grapher.InterfaceNode;
import com.google.inject.grapher.NameFactory;
-import com.google.inject.grapher.NodeAliasFactory;
-import com.google.inject.grapher.NodeIdFactory;
-import com.google.inject.grapher.Renderer;
import com.google.inject.grapher.ShortNameFactory;
-import com.google.inject.grapher.StringNodeIdFactory;
/**
- * Module that provides {@link GraphvizRenderer} as the {@link Renderer} and
- * binds the other Graphviz factories.
+ * Module that provides classes needed by {@link GraphvizGrapher}.
*
* @author phopkins@gmail.com (Pete Hopkins)
*/
public class GraphvizModule extends AbstractModule {
- @Override
- protected void configure() {
- bind(Renderer.class).to(GraphvizRenderer.class);
- bind(new TypeLiteral<NodeAliasFactory<String>>() {}).to(GraphvizRenderer.class);
- bind(GraphvizRenderer.class).in(Singleton.class);
-
- bind(NameFactory.class).to(ShortNameFactory.class);
- bind(new TypeLiteral<NodeIdFactory<String>>() {}).to(StringNodeIdFactory.class);
- bind(PortIdFactory.class).to(PortIdFactoryImpl.class);
-
- bind(new TypeLiteral<BindingEdge.Factory<String, BindingEdge<String>>>() {})
- .to(BindingEdgeFactory.class);
- bind(new TypeLiteral<DependencyEdge.Factory<String, DependencyEdge<String>>>() {})
- .to(DependencyEdgeFactory.class);
- bind(new TypeLiteral<InterfaceNode.Factory<String, InterfaceNode<String>>>() {})
- .to(InterfaceNodeFactory.class);
- bind(new TypeLiteral<ImplementationNode.Factory<String, ImplementationNode<String>>>() {})
- .to(ImplementationNodeFactory.class);
+ @Override protected void configure() {
+ bind(NameFactory.class)
+ .annotatedWith(Graphviz.class)
+ .to(ShortNameFactory.class);
+ bind(PortIdFactory.class)
+ .annotatedWith(Graphviz.class)
+ .to(PortIdFactoryImpl.class);
}
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizNode.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizNode.java
index 8a55432..ed94c17 100644
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizNode.java
+++ b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizNode.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import com.google.inject.grapher.NodeId;
import java.util.List;
import java.util.Map;
@@ -30,7 +31,7 @@
* @author phopkins@gmail.com (Pete Hopkins)
*/
public class GraphvizNode {
- private final String nodeId;
+ private final NodeId nodeId;
private NodeStyle style = NodeStyle.SOLID;
private NodeShape shape = NodeShape.BOX;
@@ -41,14 +42,16 @@
private String headerTextColor = "#000000";
private String headerBackgroundColor = "#ffffff";
+ private String identifier;
+
/** {@link Map} from port ID to field title */
private Map<String, String> fields = Maps.newLinkedHashMap();
- public GraphvizNode(String nodeId) {
+ public GraphvizNode(NodeId nodeId) {
this.nodeId = nodeId;
}
- public String getNodeId() {
+ public NodeId getNodeId() {
return nodeId;
}
@@ -107,4 +110,12 @@
public Map<String, String> getFields() {
return ImmutableMap.copyOf(fields);
}
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizRenderer.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizRenderer.java
deleted file mode 100644
index 1402133..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/GraphvizRenderer.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher.graphviz;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.inject.grapher.ImplementationNode;
-import com.google.inject.grapher.NodeAliasFactory;
-import com.google.inject.grapher.Renderer;
-
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * {@link Renderer} implementation that writes out a Graphviz DOT file of the
- * graph. Bound in {@link GraphvizModule}.
- * <p>
- * Specify the {@link PrintWriter} to output to with
- * {@link #setOut(PrintWriter)}.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class GraphvizRenderer implements Renderer, NodeAliasFactory<String> {
- private final List<GraphvizNode> nodes = Lists.newArrayList();
- private final List<GraphvizEdge> edges = Lists.newArrayList();
- private final Map<String, String> aliases = Maps.newHashMap();
-
- private PrintWriter out;
- private String rankdir = "TB";
-
- public GraphvizRenderer setOut(PrintWriter out) {
- this.out = out;
- return this;
- }
-
- public GraphvizRenderer setRankdir(String rankdir) {
- this.rankdir = rankdir;
- return this;
- }
-
- public void addNode(GraphvizNode node) {
- nodes.add(node);
- }
-
- public void addEdge(GraphvizEdge edge) {
- edges.add(edge);
- }
-
- public void newAlias(String fromId, String toId) {
- aliases.put(fromId, toId);
- }
-
- protected String resolveAlias(String id) {
- while (aliases.containsKey(id)) {
- id = aliases.get(id);
- }
-
- return id;
- }
-
- public void render() {
- start();
-
- for (GraphvizNode node : nodes) {
- renderNode(node);
- }
-
- for (GraphvizEdge edge : edges) {
- renderEdge(edge);
- }
-
- finish();
-
- out.flush();
- }
-
- protected Map<String, String> getGraphAttributes() {
- Map<String, String> attrs = Maps.newHashMap();
- attrs.put("rankdir", rankdir);
- return attrs;
- }
-
- protected void start() {
- out.println("digraph injector {");
-
- Map<String, String> attrs = getGraphAttributes();
- out.println("graph " + getAttrString(attrs) + ";");
- }
-
- protected void finish() {
- out.println("}");
- }
-
- protected void renderNode(GraphvizNode node) {
- Map<String, String> attrs = getNodeAttributes(node);
- out.println(node.getNodeId() + " " + getAttrString(attrs));
- }
-
- protected Map<String, String> getNodeAttributes(GraphvizNode node) {
- Map<String, String> attrs = Maps.newHashMap();
-
- attrs.put("label", getNodeLabel(node));
- // remove most of the margin because the table has internal padding
- attrs.put("margin", "0.02,0");
- attrs.put("shape", node.getShape().toString());
- attrs.put("style", node.getStyle().toString());
-
- return attrs;
- }
-
- /**
- * Creates the "label" for a node. This is a string of HTML that defines a
- * table with a heading at the top and (in the case of
- * {@link ImplementationNode}s) rows for each of the member fields.
- */
- protected String getNodeLabel(GraphvizNode node) {
- String cellborder = node.getStyle() == NodeStyle.INVISIBLE ? "1" : "0";
-
- StringBuilder html = new StringBuilder();
- html.append("<");
- html.append("<table cellspacing=\"0\" cellpadding=\"5\" cellborder=\"");
- html.append(cellborder).append("\" border=\"0\">");
-
- html.append("<tr>").append("<td align=\"left\" port=\"header\" ");
- html.append("bgcolor=\"" + node.getHeaderBackgroundColor() + "\">");
-
- String subtitle = Joiner.on("<br align=\"left\"/>").join(node.getSubtitles());
- if (subtitle.length() != 0) {
- html.append("<font color=\"").append(node.getHeaderTextColor());
- html.append("\" point-size=\"10\">");
- html.append(subtitle).append("<br align=\"left\"/>").append("</font>");
- }
-
- html.append("<font color=\"" + node.getHeaderTextColor() + "\">");
- html.append(htmlEscape(node.getTitle())).append("<br align=\"left\"/>");
- html.append("</font>").append("</td>").append("</tr>");
-
- for (Map.Entry<String, String> field : node.getFields().entrySet()) {
- html.append("<tr>");
- html.append("<td align=\"left\" port=\"").append(field.getKey()).append("\">");
- html.append(htmlEscape(field.getValue()));
- html.append("</td>").append("</tr>");
- }
-
- html.append("</table>");
- html.append(">");
- return html.toString();
- }
-
- protected void renderEdge(GraphvizEdge edge) {
- Map<String, String> attrs = getEdgeAttributes(edge);
-
- String tailId = getEdgeEndPoint(resolveAlias(edge.getTailNodeId()), edge.getTailPortId(),
- edge.getTailCompassPoint());
-
- String headId = getEdgeEndPoint(resolveAlias(edge.getHeadNodeId()), edge.getHeadPortId(),
- edge.getHeadCompassPoint());
-
- out.println(tailId + " -> " + headId + " " + getAttrString(attrs));
- }
-
- protected Map<String, String> getEdgeAttributes(GraphvizEdge edge) {
- Map<String, String> attrs = Maps.newHashMap();
-
- attrs.put("arrowhead", getArrowString(edge.getArrowHead()));
- attrs.put("arrowtail", getArrowString(edge.getArrowTail()));
- attrs.put("style", edge.getStyle().toString());
-
- return attrs;
- }
-
- private String getAttrString(Map<String, String> attrs) {
- List<String> attrList = Lists.newArrayList();
-
- for (Entry<String, String> attr : attrs.entrySet()) {
- String value = attr.getValue();
-
- if (value != null) {
- attrList.add(attr.getKey() + "=" + value);
- }
- }
-
- return "[" + Joiner.on(", ").join(attrList) + "]";
- }
-
- /**
- * Turns a {@link List} of {@link ArrowType}s into a {@link String} that
- * represents combining them. With Graphviz, that just means concatenating
- * them.
- */
- protected String getArrowString(List<ArrowType> arrows) {
- return Joiner.on("").join(arrows);
- }
-
- protected String getEdgeEndPoint(String nodeId, String portId, CompassPoint compassPoint) {
- List<String> portStrings = Lists.newArrayList(nodeId);
-
- if (portId != null) {
- portStrings.add(portId);
- }
-
- if (compassPoint != null) {
- portStrings.add(compassPoint.toString());
- }
-
- return Joiner.on(":").join(portStrings);
- }
-
- protected String htmlEscape(String str) {
- return str.replace("&", "&").replace("<", "<").replace(">", ">");
- }
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/ImplementationNodeFactory.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/ImplementationNodeFactory.java
deleted file mode 100644
index 5813f8f..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/ImplementationNodeFactory.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher.graphviz;
-
-import com.google.inject.Inject;
-import com.google.inject.Key;
-import com.google.inject.grapher.ImplementationNode;
-import com.google.inject.grapher.NameFactory;
-import com.google.inject.grapher.graphviz.BindingEdgeFactory.GraphvizEdgeAdaptor;
-
-import java.lang.reflect.Member;
-
-/**
- * Graphviz-specific implementation of {@link ImplementationNode.Factory}. Uses
- * a {@link GraphvizEdgeAdaptor} to delegate to a {@link GraphvizNode}.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class ImplementationNodeFactory
-implements ImplementationNode.Factory<String, ImplementationNode<String>> {
- private final GraphvizRenderer renderer;
- private final NameFactory nameFactory;
- private final PortIdFactory portIdFactory;
-
- @Inject
- public ImplementationNodeFactory(GraphvizRenderer renderer, NameFactory nameFactory,
- PortIdFactory portIdFactory) {
- this.renderer = renderer;
- this.nameFactory = nameFactory;
- this.portIdFactory = portIdFactory;
- }
-
- public ImplementationNode<String> newImplementationNode(String nodeId) {
- GraphvizNode node = new GraphvizNode(nodeId);
-
- renderer.addNode(node);
- return newAdaptor(node);
- }
-
- protected GraphvizNodeAdaptor newAdaptor(GraphvizNode node) {
- return new GraphvizNodeAdaptor(node);
- }
-
- /**
- * Adaptor class that converts {@link ImplementationNode} methods to display
- * operations on a {@link GraphvizNode}.
- */
- protected class GraphvizNodeAdaptor implements ImplementationNode<String> {
- protected final GraphvizNode node;
-
- public GraphvizNodeAdaptor(GraphvizNode node) {
- this.node = node;
-
- this.node.setStyle(NodeStyle.INVISIBLE);
- }
-
- public void setClassKey(Key<?> key) {
- node.setHeaderBackgroundColor("#000000");
- node.setHeaderTextColor("#ffffff");
- node.setTitle(nameFactory.getClassName(key));
- }
-
- public void setInstance(Object instance) {
- node.setHeaderBackgroundColor("#aaaaaa");
- node.setHeaderTextColor("#ffffff");
- node.setTitle(nameFactory.getInstanceName(instance));
- }
-
- public void setSource(Object source) {
- node.addSubtitle(0, nameFactory.getSourceName(source));
- }
-
- public void addMember(Member member) {
- node.addField(portIdFactory.getPortId(member), nameFactory.getMemberName(member));
- }
- }
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/InterfaceNodeFactory.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/InterfaceNodeFactory.java
deleted file mode 100644
index 10bb3c1..0000000
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/InterfaceNodeFactory.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher.graphviz;
-
-import com.google.inject.Inject;
-import com.google.inject.Key;
-import com.google.inject.grapher.InterfaceNode;
-import com.google.inject.grapher.NameFactory;
-
-/**
- * Graphviz-specific implementation of {@link InterfaceNode.Factory}. Uses
- * a {@link GraphvizEdgeAdaptor} to delegate to a {@link GraphvizNode}.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class InterfaceNodeFactory implements InterfaceNode.Factory<String, InterfaceNode<String>> {
- private final GraphvizRenderer renderer;
- private final NameFactory nameFactory;
-
- @Inject
- public InterfaceNodeFactory(GraphvizRenderer renderer, NameFactory nameFactory) {
- this.renderer = renderer;
- this.nameFactory = nameFactory;
- }
-
- public InterfaceNode<String> newInterfaceNode(String nodeId) {
- GraphvizNode node = new GraphvizNode(nodeId);
-
- renderer.addNode(node);
- return newAdaptor(node);
- }
-
- private GraphvizNodeAdaptor newAdaptor(GraphvizNode node) {
- return new GraphvizNodeAdaptor(node);
- }
-
- /**
- * Adaptor class that converts {@link InterfaceNode} methods to display
- * operations on a {@link GraphvizNode}.
- */
- protected class GraphvizNodeAdaptor implements InterfaceNode<String> {
- protected final GraphvizNode node;
-
- public GraphvizNodeAdaptor(GraphvizNode node) {
- this.node = node;
-
- this.node.setStyle(NodeStyle.DASHED);
- }
-
- public void setKey(Key<?> key) {
- String title = nameFactory.getClassName(key);
- node.setTitle(title);
- node.addSubtitle(0, nameFactory.getAnnotationName(key));
- }
-
- public void setSource(Object source) {
- // TODO(phopkins): Show the Module on the graph, which comes from the
- // class name when source is a StackTraceElement.
- }
- }
-}
diff --git a/extensions/grapher/src/com/google/inject/grapher/graphviz/PortIdFactory.java b/extensions/grapher/src/com/google/inject/grapher/graphviz/PortIdFactory.java
index cfb2fe1..bc5deda 100644
--- a/extensions/grapher/src/com/google/inject/grapher/graphviz/PortIdFactory.java
+++ b/extensions/grapher/src/com/google/inject/grapher/graphviz/PortIdFactory.java
@@ -16,14 +16,11 @@
package com.google.inject.grapher.graphviz;
-import com.google.inject.grapher.StringNodeIdFactory;
-
import java.lang.reflect.Member;
/**
* Interface for a service that returns Graphviz port IDs, used for naming the
* rows in {@link ImplementationNode}-displaying {@link GraphvizNode}s.
- * Implemented by {@link StringNodeIdFactory}.
*
* @author phopkins@gmail.com (Pete Hopkins)
*/
diff --git a/extensions/grapher/test/com/google/inject/grapher/AbstractInjectorGrapherTest.java b/extensions/grapher/test/com/google/inject/grapher/AbstractInjectorGrapherTest.java
new file mode 100644
index 0000000..8d90f4c
--- /dev/null
+++ b/extensions/grapher/test/com/google/inject/grapher/AbstractInjectorGrapherTest.java
@@ -0,0 +1,184 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.inject.AbstractModule;
+import com.google.inject.BindingAnnotation;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.spi.InjectionPoint;
+import com.google.testing.testsize.MediumTest;
+
+import junit.framework.TestCase;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Member;
+import java.util.Set;
+
+/**
+ * Test cases for {@link AbstractInjectorGrapher}. This indirectly tests most classes in this
+ * package.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+@MediumTest
+public class AbstractInjectorGrapherTest extends TestCase {
+ private static final String TEST_STRING = "test";
+
+ private static class FakeGrapher extends AbstractInjectorGrapher {
+ final Set<Node> nodes = Sets.newHashSet();
+ final Set<Edge> edges = Sets.newHashSet();
+
+ @Override protected void reset() {
+ nodes.clear();
+ edges.clear();
+ }
+
+ @Override protected void newInterfaceNode(InterfaceNode node) {
+ assertFalse(nodes.contains(node));
+ nodes.add(node);
+ }
+
+ @Override protected void newImplementationNode(ImplementationNode node) {
+ assertFalse(nodes.contains(node));
+ nodes.add(node);
+ }
+
+ @Override protected void newInstanceNode(InstanceNode node) {
+ assertFalse(nodes.contains(node));
+ nodes.add(node);
+ }
+
+ @Override protected void newDependencyEdge(DependencyEdge edge) {
+ assertFalse(edges.contains(edge));
+ edges.add(edge);
+ }
+
+ @Override protected void newBindingEdge(BindingEdge edge) {
+ assertFalse(edges.contains(edge));
+ edges.add(edge);
+ }
+
+ @Override protected void postProcess() {}
+ }
+
+ private static final class Wrapper<T> {
+ T value;
+ }
+
+ @BindingAnnotation
+ @Retention(RetentionPolicy.RUNTIME)
+ private static @interface Ann {}
+ private static interface IA {}
+ private static class A implements IA {
+ @Inject public A(String str) {}
+ }
+ private static class A2 implements IA {
+ @Inject public A2(Provider<String> strProvider) {}
+ }
+
+ private Node aNode;
+ private Node a2Node;
+ private Node iaNode;
+ private Node iaAnnNode;
+ private Node stringNode;
+ private Node stringInstanceNode;
+
+ private FakeGrapher grapher;
+
+ @Override protected void setUp() throws Exception {
+ super.setUp();
+ grapher = new FakeGrapher();
+ Node.ignoreSourceInComparisons = true;
+ aNode = new ImplementationNode(NodeId.newTypeId(Key.get(A.class)), null,
+ ImmutableList.<Member>of(A.class.getConstructor(String.class)));
+ a2Node = new ImplementationNode(NodeId.newTypeId(Key.get(A2.class)), null,
+ ImmutableList.<Member>of(A2.class.getConstructor(Provider.class)));
+ iaNode = new InterfaceNode(NodeId.newTypeId(Key.get(IA.class)), null);
+ iaAnnNode = new InterfaceNode(NodeId.newTypeId(Key.get(IA.class, Ann.class)), null);
+ stringNode = new InterfaceNode(NodeId.newTypeId(Key.get(String.class)), null);
+ stringInstanceNode = new InstanceNode(NodeId.newInstanceId(Key.get(String.class)), null,
+ TEST_STRING, ImmutableList.<Member>of());
+ }
+
+ public void testLinkedAndInstanceBindings() throws Exception {
+ grapher.graph(Guice.createInjector(new AbstractModule() {
+ @Override protected void configure() {
+ bind(IA.class).to(A.class);
+ bind(IA.class).annotatedWith(Ann.class).to(A.class);
+ bind(String.class).toInstance(TEST_STRING);
+ }
+ }));
+
+ Set<Node> expectedNodes =
+ ImmutableSet.<Node>of(iaNode, iaAnnNode, aNode, stringNode, stringInstanceNode);
+ Set<Edge> expectedEdges = ImmutableSet.<Edge>of(
+ new BindingEdge(iaNode.getId(), aNode.getId(), BindingEdge.Type.NORMAL),
+ new BindingEdge(iaAnnNode.getId(), aNode.getId(), BindingEdge.Type.NORMAL),
+ new BindingEdge(stringNode.getId(), stringInstanceNode.getId(), BindingEdge.Type.NORMAL),
+ new DependencyEdge(aNode.getId(), stringNode.getId(),
+ InjectionPoint.forConstructor(A.class.getConstructor(String.class))));
+ assertEquals(expectedNodes, grapher.nodes);
+ assertEquals(expectedEdges, grapher.edges);
+ }
+
+ public void testProviderBindings() throws Exception {
+ final Wrapper<Provider<A2>> wrapper = new Wrapper<Provider<A2>>();
+ grapher.graph(Guice.createInjector(new AbstractModule() {
+ @Override protected void configure() {
+ wrapper.value = getProvider(A2.class);
+ bind(IA.class).toProvider(wrapper.value);
+ bind(A2.class);
+ bind(String.class).toInstance(TEST_STRING);
+ }
+ }));
+
+ Node a2ProviderNode = new InstanceNode(NodeId.newInstanceId(Key.get(IA.class)), null,
+ wrapper.value, ImmutableList.<Member>of());
+ Set<Node> expectedNodes =
+ ImmutableSet.<Node>of(iaNode, stringNode, a2Node, stringInstanceNode, a2ProviderNode);
+ Set<Edge> expectedEdges = ImmutableSet.<Edge>of(
+ new BindingEdge(stringNode.getId(), stringInstanceNode.getId(), BindingEdge.Type.NORMAL),
+ new BindingEdge(iaNode.getId(), a2ProviderNode.getId(), BindingEdge.Type.PROVIDER),
+ new DependencyEdge(a2Node.getId(), stringNode.getId(),
+ InjectionPoint.forConstructor(A2.class.getConstructor(Provider.class))));
+ assertEquals(expectedNodes, grapher.nodes);
+ assertEquals(expectedEdges, grapher.edges);
+ }
+
+ public void testGraphWithGivenRoot() throws Exception {
+ grapher.graph(Guice.createInjector(new AbstractModule() {
+ @Override protected void configure() {
+ bind(IA.class).to(A.class);
+ bind(IA.class).annotatedWith(Ann.class).to(A.class);
+ bind(String.class).toInstance(TEST_STRING);
+ }
+ }), ImmutableSet.<Key<?>>of(Key.get(String.class)));
+
+ Set<Node> expectedNodes = ImmutableSet.<Node>of(stringNode, stringInstanceNode);
+ Set<Edge> expectedEdges = ImmutableSet.<Edge>of(
+ new BindingEdge(stringNode.getId(), stringInstanceNode.getId(), BindingEdge.Type.NORMAL));
+ assertEquals(expectedNodes, grapher.nodes);
+ assertEquals(expectedEdges, grapher.edges);
+ }
+}
diff --git a/extensions/grapher/test/com/google/inject/grapher/AllTests.java b/extensions/grapher/test/com/google/inject/grapher/AllTests.java
index 1617d69..af81398 100644
--- a/extensions/grapher/test/com/google/inject/grapher/AllTests.java
+++ b/extensions/grapher/test/com/google/inject/grapher/AllTests.java
@@ -26,7 +26,7 @@
public static Test suite() {
TestSuite suite = new TestSuite();
- suite.addTestSuite(GraphingVisitorTest.class);
+ suite.addTestSuite(AbstractInjectorGrapherTest.class);
suite.addTestSuite(ShortNameFactoryTest.class);
suite.addTestSuite(TransitiveDependencyVisitorTest.class);
return suite;
diff --git a/extensions/grapher/test/com/google/inject/grapher/GraphingVisitorTest.java b/extensions/grapher/test/com/google/inject/grapher/GraphingVisitorTest.java
deleted file mode 100644
index 7deb3fc..0000000
--- a/extensions/grapher/test/com/google/inject/grapher/GraphingVisitorTest.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
- * Copyright (C) 2008 Google Inc.
- *
- * 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 com.google.inject.grapher;
-
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.inject.AbstractModule;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.spi.ConstructorBinding;
-import com.google.inject.spi.Dependency;
-import com.google.inject.spi.HasDependencies;
-import com.google.inject.spi.InjectionPoint;
-import com.google.inject.spi.InstanceBinding;
-
-import junit.framework.TestCase;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Tests for {@link GraphingVisitor}.
- *
- * @author phopkins@gmail.com (Pete Hopkins)
- */
-public class GraphingVisitorTest extends TestCase {
- private NodeIdFactory<String> nodeIdFactory;
- private InterfaceNode.Factory<String, InterfaceNode<String>> interfaceNodeFactory;
- private ImplementationNode.Factory<String, ImplementationNode<String>> implementationNodeFactory;
- private BindingEdge.Factory<String, BindingEdge<String>> bindingEdgeFactory;
- private DependencyEdge.Factory<String, DependencyEdge<String>> dependencyEdgeFactory;
- private NodeAliasFactory<String> nodeAliasFactory;
-
- private GraphingVisitor<String, InterfaceNode<String>, ImplementationNode<String>,
- BindingEdge<String>, DependencyEdge<String>> graphingVisitor;
-
- private List<Object> mocks;
-
- @SuppressWarnings("unchecked")
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- mocks = Lists.newArrayList();
-
- nodeIdFactory = new StringNodeIdFactory();
- interfaceNodeFactory = recordMock(createMock(InterfaceNode.Factory.class));
- implementationNodeFactory = recordMock(createMock(ImplementationNode.Factory.class));
- bindingEdgeFactory = recordMock(createMock(BindingEdge.Factory.class));
- dependencyEdgeFactory = new DependencyEdgeFactory();
- nodeAliasFactory = recordMock(createMock(NodeAliasFactory.class));
-
- graphingVisitor = new GraphingVisitor<String, InterfaceNode<String>,
- ImplementationNode<String>, BindingEdge<String>, DependencyEdge<String>>(
- nodeIdFactory, interfaceNodeFactory, implementationNodeFactory,
- bindingEdgeFactory, dependencyEdgeFactory, nodeAliasFactory);
- }
-
- private <T> T recordMock(T mock) {
- mocks.add(mock);
- return mock;
- }
-
- public void testNewDependencies_withInjectionPoints() throws Exception {
- @SuppressWarnings("unchecked")
- ImplementationNode<String> node = recordMock(createMock(ImplementationNode.class));
-
- node.addMember(Obj.class.getDeclaredField("string"));
- expectLastCall();
- node.addMember(Obj.class.getDeclaredField("integer"));
- expectLastCall();
- node.addMember(Obj.class.getDeclaredField("bool"));
- expectLastCall();
- node.addMember(Obj.class.getDeclaredMethod("setInteger", Integer.class));
- expectLastCall();
-
- replayAll();
-
- Injector injector = Guice.createInjector(new ClassModule());
- ConstructorBinding<?> binding = (ConstructorBinding<?>) injector.getBinding(Obj.class);
-
- Collection<DependencyEdge<String>> edges = graphingVisitor.newDependencyEdges("", node,
- binding.getDependencies());
-
- assertEquals("There should be four edges, from the three fields plus the method",
- 4, edges.size());
-
- verifyAll();
- }
-
- public void testNewDependencies_withDependencies() throws Exception {
- @SuppressWarnings("unchecked")
- ImplementationNode<String> node = recordMock(createMock(ImplementationNode.class));
- // No members should be added to the node, since the stated dependencies
- // have no associated {@link InjectionPoint}s.
-
- replayAll();
-
- Injector injector = Guice.createInjector(new ClassModule(), new InstanceModule());
- InstanceBinding<?> binding = (InstanceBinding<?>) injector.getBinding(Obj.class);
-
- Collection<DependencyEdge<String>> edges = graphingVisitor.newDependencyEdges("", node,
- binding.getDependencies());
-
- assertEquals("One edge should be created, for the one stated Integer dependency",
- 1, edges.size());
-
- verifyAll();
- }
-
- private void replayAll() {
- for (Object mock : mocks) {
- replay(mock);
- }
- }
-
- private void verifyAll() {
- for (Object mock : mocks) {
- verify(mock);
- }
- }
-
- private class DependencyEdgeFactory
- implements DependencyEdge.Factory<String, DependencyEdge<String>> {
- public DependencyEdge<String> newDependencyEdge(String fromId,
- InjectionPoint fromPoint, String toId) {
- @SuppressWarnings("unchecked")
- DependencyEdge<String> edge = createMock(DependencyEdge.class);
- return edge;
- }
- }
-
- private static class ClassModule extends AbstractModule {
- @Override
- protected void configure() {
- bind(String.class).toInstance("String");
- bind(Integer.class).toInstance(Integer.valueOf(8));
- bind(Boolean.class).toInstance(Boolean.TRUE);
- }
- }
-
- private static class InstanceModule extends AbstractModule {
- @Override
- protected void configure() {
- bind(Obj.class).toInstance(new Obj());
- }
- }
-
- private static class Obj implements HasDependencies {
- @Inject String string;
- @Inject Integer integer;
- @Inject Boolean bool;
-
- @Inject void setInteger(Integer integer) {}
-
- public Set<Dependency<?>> getDependencies() {
- return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Integer.class)));
- }
- }
-}
diff --git a/extensions/grapher/test/com/google/inject/grapher/InjectorGrapherTest.java b/extensions/grapher/test/com/google/inject/grapher/InjectorGrapherTest.java
deleted file mode 100644
index 2df6b9d..0000000
--- a/extensions/grapher/test/com/google/inject/grapher/InjectorGrapherTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-
-package com.google.inject.grapher;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.inject.AbstractModule;
-import com.google.inject.Binding;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.Provides;
-import com.google.inject.name.Named;
-import com.google.inject.name.Names;
-import com.google.inject.spi.BindingTargetVisitor;
-import com.google.inject.spi.ConstructorBinding;
-import com.google.inject.spi.ConvertedConstantBinding;
-import com.google.inject.spi.ExposedBinding;
-import com.google.inject.spi.InstanceBinding;
-import com.google.inject.spi.LinkedKeyBinding;
-import com.google.inject.spi.ProviderBinding;
-import com.google.inject.spi.ProviderInstanceBinding;
-import com.google.inject.spi.ProviderKeyBinding;
-import com.google.inject.spi.UntargettedBinding;
-
-import junit.framework.TestCase;
-
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * Tests for {@link InjectorGrapher}.
- *
- * @author bojand@google.com (Bojan Djordjevic)
- */
-public class InjectorGrapherTest extends TestCase {
- private static class FakeGraphingVisitor implements BindingTargetVisitor<Object, Void> {
- private final Set<Key> keys = Sets.newHashSet();
-
- public Void visit(InstanceBinding<?> binding) {
- return record(binding);
- }
- public Void visit(ProviderInstanceBinding<?> binding) {
- return record(binding);
- }
- public Void visit(ProviderKeyBinding<?> binding) {
- return record(binding);
- }
- public Void visit(LinkedKeyBinding<?> binding) {
- return record(binding);
- }
- public Void visit(ExposedBinding<?> binding) {
- return record(binding);
- }
- public Void visit(UntargettedBinding<?> binding) {
- return record(binding);
- }
- public Void visit(ConstructorBinding<?> binding) {
- return record(binding);
- }
- public Void visit(ConvertedConstantBinding<?> binding) {
- return record(binding);
- }
- public Void visit(ProviderBinding<?> binding) {
- return record(binding);
- }
-
- public Set<Key> getKeys() {
- return keys;
- }
-
- private Void record(Binding<?> binding) {
- keys.add(binding.getKey());
- return null;
- }
- }
-
- private static class A {}
- private static class B {}
- private static class C {}
- private static class D {}
- private static class E {}
-
- private static class ClassModule extends AbstractModule {
- @Override protected void configure() {
- bind(D.class).toInstance(new D());
- bind(E.class).toInstance(new E());
- }
-
- @Provides A provideA(B b, @Named("test") C c) {
- return new A();
- }
-
- @Provides B provideB(D d, E e) {
- return new B();
- }
-
- @Provides @Named("test") C provideC(D d, E e) {
- return new C();
- }
- }
-
- private final BindingTargetVisitor<Object, Collection<Key<?>>> keyVisitor =
- new TransitiveDependencyVisitor();
- private final Renderer renderer = new Renderer() {
- public void render() {}
- };
-
- private FakeGraphingVisitor graphingVisitor;
- private Injector injector;
- private InjectorGrapher grapher;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- graphingVisitor = new FakeGraphingVisitor();
- injector = Guice.createInjector(new ClassModule());
- grapher = new InjectorGrapher(keyVisitor, graphingVisitor, renderer);
- }
-
- /** Tests making a graph rooted at a {@link Class}. */
- public void testRootedAtClass() throws Exception {
- grapher.of(injector)
- .rootedAt(B.class)
- .graph();
- assertEquals(ImmutableSet.<Key<?>>of(
- Key.get(B.class),
- Key.get(D.class),
- Key.get(E.class)), graphingVisitor.getKeys());
- }
-
- /** Tests making a graph rooted at a {@link Key}. */
- public void testRootedAtKey() throws Exception {
- Key cKey = Key.get(C.class, Names.named("test"));
- grapher.of(injector)
- .rootedAt(cKey)
- .graph();
- assertEquals(ImmutableSet.of(
- cKey,
- Key.get(D.class),
- Key.get(E.class)), graphingVisitor.getKeys());
- }
-}
\ No newline at end of file
diff --git a/extensions/grapher/test/com/google/inject/grapher/demo/AssistedInjectModule.java b/extensions/grapher/test/com/google/inject/grapher/demo/AssistedInjectModule.java
index 2d81957..95a83c1 100644
--- a/extensions/grapher/test/com/google/inject/grapher/demo/AssistedInjectModule.java
+++ b/extensions/grapher/test/com/google/inject/grapher/demo/AssistedInjectModule.java
@@ -17,7 +17,7 @@
package com.google.inject.grapher.demo;
import com.google.inject.AbstractModule;
-import com.google.inject.assistedinject.FactoryProvider;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
/**
* Module to add {@link AssistedInject}-based elements to the demo
@@ -28,7 +28,8 @@
public class AssistedInjectModule extends AbstractModule {
@Override
protected void configure() {
- bind(DancePartyFactory.class).toProvider(
- FactoryProvider.newFactory(DancePartyFactory.class, DancePartyImpl.class));
+ install(new FactoryModuleBuilder()
+ .implement(DanceParty.class, DancePartyImpl.class)
+ .build(DancePartyFactory.class));
}
}
diff --git a/extensions/grapher/test/com/google/inject/grapher/demo/DeLorian.java b/extensions/grapher/test/com/google/inject/grapher/demo/DeLorian.java
index c973407..cf3537e 100644
--- a/extensions/grapher/test/com/google/inject/grapher/demo/DeLorian.java
+++ b/extensions/grapher/test/com/google/inject/grapher/demo/DeLorian.java
@@ -24,7 +24,8 @@
// between a Provider<T> and just @Injecting T.
@Inject @Driver Provider<Person> driver;
@Inject FluxCapacitor fluxCapacitor;
+ @Inject PrivateTestModule.Exposed exposed;
@Inject
public void setEnergySource(EnergySource energySource) {}
-}
\ No newline at end of file
+}
diff --git a/extensions/grapher/test/com/google/inject/grapher/demo/InjectorGrapherDemo.java b/extensions/grapher/test/com/google/inject/grapher/demo/InjectorGrapherDemo.java
index ce285c2..1e36dd5 100644
--- a/extensions/grapher/test/com/google/inject/grapher/demo/InjectorGrapherDemo.java
+++ b/extensions/grapher/test/com/google/inject/grapher/demo/InjectorGrapherDemo.java
@@ -19,10 +19,8 @@
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Stage;
-import com.google.inject.grapher.GrapherModule;
-import com.google.inject.grapher.InjectorGrapher;
+import com.google.inject.grapher.graphviz.GraphvizGrapher;
import com.google.inject.grapher.graphviz.GraphvizModule;
-import com.google.inject.grapher.graphviz.GraphvizRenderer;
import java.io.File;
import java.io.PrintWriter;
@@ -38,15 +36,13 @@
public static void main(String[] args) throws Exception {
// TODO(phopkins): Switch to Stage.TOOL when issue 297 is fixed.
Injector demoInjector = Guice.createInjector(Stage.DEVELOPMENT,
- new BackToTheFutureModule(), new MultibinderModule());
+ new BackToTheFutureModule(), new MultibinderModule(), new PrivateTestModule());
PrintWriter out = new PrintWriter(new File(args[0]), "UTF-8");
- Injector injector = Guice.createInjector(new GrapherModule(), new GraphvizModule());
- GraphvizRenderer renderer = injector.getInstance(GraphvizRenderer.class);
- renderer.setOut(out).setRankdir("TB");
-
- injector.getInstance(InjectorGrapher.class)
- .of(demoInjector)
- .graph();
+ Injector injector = Guice.createInjector(new GraphvizModule());
+ GraphvizGrapher grapher = injector.getInstance(GraphvizGrapher.class);
+ grapher.setOut(out);
+ grapher.setRankdir("TB");
+ grapher.graph(demoInjector);
}
}
diff --git a/extensions/grapher/test/com/google/inject/grapher/demo/PrivateTestModule.java b/extensions/grapher/test/com/google/inject/grapher/demo/PrivateTestModule.java
new file mode 100644
index 0000000..7619897
--- /dev/null
+++ b/extensions/grapher/test/com/google/inject/grapher/demo/PrivateTestModule.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 com.google.inject.grapher.demo;
+
+import com.google.inject.PrivateModule;
+
+/**
+ * Module to test private modules and exposed bindings.
+ *
+ * @author bojand@google.com (Bojan Djordjevic)
+ */
+public class PrivateTestModule extends PrivateModule {
+ interface Exposed {}
+ static class Hidden implements Exposed {}
+
+ @Override protected void configure() {
+ bind(Exposed.class).to(Hidden.class);
+ expose(Exposed.class);
+ }
+}