1 /*
2  * Copyright (C) 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.grapher;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.Lists;
21 import com.google.inject.Binding;
22 import com.google.inject.spi.ConstructorBinding;
23 import com.google.inject.spi.DefaultBindingTargetVisitor;
24 import com.google.inject.spi.Dependency;
25 import com.google.inject.spi.HasDependencies;
26 import com.google.inject.spi.InjectionPoint;
27 import com.google.inject.spi.InstanceBinding;
28 import com.google.inject.spi.ProviderInstanceBinding;
29 import java.lang.reflect.Member;
30 import java.util.Collection;
31 import java.util.List;
32 
33 /**
34  * Default node creator.
35  *
36  * @author bojand@google.com (Bojan Djordjevic)
37  */
38 final class DefaultNodeCreator implements NodeCreator {
39   @Override
getNodes(Iterable<Binding<?>> bindings)40   public Iterable<Node> getNodes(Iterable<Binding<?>> bindings) {
41     List<Node> nodes = Lists.newArrayList();
42     NodeVisitor visitor = new NodeVisitor();
43     for (Binding<?> binding : bindings) {
44       nodes.addAll(binding.acceptTargetVisitor(visitor));
45     }
46     return nodes;
47   }
48 
49   /**
50    * {@link BindingTargetVisitor} that adds nodes to the graph based on the visited {@link Binding}.
51    */
52   private static final class NodeVisitor
53       extends DefaultBindingTargetVisitor<Object, Collection<Node>> {
54 
55     /** Returns a new interface node for the given {@link Binding}. */
newInterfaceNode(Binding<?> binding)56     private InterfaceNode newInterfaceNode(Binding<?> binding) {
57       return new InterfaceNode(NodeId.newTypeId(binding.getKey()), binding.getSource());
58     }
59 
60     /**
61      * Returns a new implementation node for the given binding.
62      *
63      * @param binding binding for the node to create
64      * @param members members to add to the node
65      * @return implementation node for the given binding
66      */
newImplementationNode( Binding<?> binding, Collection<Member> members)67     private ImplementationNode newImplementationNode(
68         Binding<?> binding, Collection<Member> members) {
69       return new ImplementationNode(
70           NodeId.newTypeId(binding.getKey()), binding.getSource(), members);
71     }
72 
73     /**
74      * Returns a new instance node for the given {@link Binding}.
75      *
76      * @param binding binding for the node to create
77      * @param instance value of the instance
78      * @return instance node for the given binding
79      */
newInstanceNode( T binding, Object instance)80     private <T extends Binding<?> & HasDependencies> InstanceNode newInstanceNode(
81         T binding, Object instance) {
82       Collection<Member> members = Lists.newArrayList();
83       for (Dependency<?> dependency : binding.getDependencies()) {
84         InjectionPoint injectionPoint = dependency.getInjectionPoint();
85 
86         if (injectionPoint != null) {
87           members.add(injectionPoint.getMember());
88         }
89       }
90       return new InstanceNode(
91           NodeId.newInstanceId(binding.getKey()), binding.getSource(), instance, members);
92     }
93 
94     /**
95      * Visitor for {@link ConstructorBinding}s. These are for classes that Guice will instantiate to
96      * satisfy injection requests.
97      */
98     @Override
visit(ConstructorBinding<?> binding)99     public Collection<Node> visit(ConstructorBinding<?> binding) {
100       Collection<Member> members = Lists.newArrayList();
101       members.add(binding.getConstructor().getMember());
102       for (InjectionPoint injectionPoint : binding.getInjectableMembers()) {
103         members.add(injectionPoint.getMember());
104       }
105 
106       return ImmutableList.<Node>of(newImplementationNode(binding, members));
107     }
108 
109     /**
110      * Visitor for {@link InstanceBinding}. We render two nodes in this case: an interface node for
111      * the binding's {@link Key}, and then an implementation node for the instance {@link Object}
112      * itself.
113      */
114     @Override
visit(InstanceBinding<?> binding)115     public Collection<Node> visit(InstanceBinding<?> binding) {
116       return ImmutableList.<Node>of(
117           newInterfaceNode(binding), newInstanceNode(binding, binding.getInstance()));
118     }
119 
120     /**
121      * Same as {@link #visit(InstanceBinding)}, but the binding edge is {@link
122      * BindingEdgeType#PROVIDER}.
123      */
124     @Override
visit(ProviderInstanceBinding<?> binding)125     public Collection<Node> visit(ProviderInstanceBinding<?> binding) {
126       return ImmutableList.<Node>of(
127           newInterfaceNode(binding), newInstanceNode(binding, binding.getUserSuppliedProvider()));
128     }
129 
130     @Override
visitOther(Binding<?> binding)131     public Collection<Node> visitOther(Binding<?> binding) {
132       return ImmutableList.<Node>of(newInterfaceNode(binding));
133     }
134   }
135 }
136