1 /*
2 * Grapht, an open source dependency injector.
3 * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4 * Copyright 2010-2014 Regents of the University of Minnesota
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program; if not, write to the Free Software Foundation, Inc., 51
18 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 package org.grouplens.grapht.solver;
21
22 import org.grouplens.grapht.*;
23 import org.grouplens.grapht.reflect.*;
24 import org.grouplens.grapht.util.InstanceProvider;
25 import org.grouplens.grapht.util.Types;
26
27 import javax.inject.Provider;
28 import java.io.*;
29 import java.lang.reflect.ParameterizedType;
30 import java.lang.reflect.Type;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Map;
34
35 /**
36 * <p>
37 * BindingFunction that enables provider-injection. This function supports
38 * just-in-time binding for injection points for Providers, that creates
39 * Providers wrapping whatever provided-type is necessary for that injection
40 * point.
41 * <p>
42 * As an example, <code>Provider<Foo></code> would have a Provider of Foo
43 * injected, and the Foo instances returned by that Provider's get() method
44 * would be configured as if the injection point was for <code>Foo</code>.
45 *
46 * @author <a href="http://grouplens.org">GroupLens Research</a>
47 */
48 public class ProviderBindingFunction implements BindingFunction {
49 public ProviderBindingFunction() {
50 }
51
52 @Override
53 public BindingResult bind(InjectionContext context, DesireChain desires) throws ResolutionException {
54 Desire desire = desires.getCurrentDesire();
55 if (Provider.class.equals(desire.getDesiredType())) {
56 // Look at the parameterized type of the injection point to
57 // find what type of object should be provided
58 Type providerType = desire.getInjectionPoint().getType();
59 if (providerType instanceof ParameterizedType) {
60 // Can only inject a Provider if it's a parameterized type,
61 // otherwise we have no type information about the provided type
62 Type[] typeArgs = ((ParameterizedType) providerType).getActualTypeArguments();
63 if (typeArgs.length == 1 && (typeArgs[0] instanceof Class ||
64 typeArgs[0] instanceof ParameterizedType)) {
65 Class<?> providedType = Types.erase(typeArgs[0]);
66
67 // Create a desire for the provided type, cloning the attributes
68 // and nullability from the original desire
69 Desire providedDesire = Desires.create(desire.getInjectionPoint().getQualifier(),
70 providedType, desire.getInjectionPoint().isNullable());
71 // Satisfied JIT desire for this injection point
72 Desire jitDesire = desire.restrict(new ProviderInjectionSatisfaction(providedDesire));
73 // Make sure to defer this binding since the single dependency
74 // on the provided type might very well create a cycle that deferred
75 // injection must break.
76 return BindingResult.newBuilder()
77 .setDesire(jitDesire)
78 .setCachePolicy(CachePolicy.NO_PREFERENCE)
79 .addFlag(BindingFlag.DEFERRED)
80 .addFlag(BindingFlag.TERMINAL)
81 .build();
82 }
83 }
84 }
85
86 // Not a Provider desire, or the type didn't have
87 // enough information to determine what we should provide
88 return null;
89 }
90
91 /**
92 * Satisfaction implementation that provides a Provider, and has a single
93 * dependency on the provided type.
94 */
95 private static class ProviderInjectionSatisfaction implements Satisfaction, Serializable {
96 private static final long serialVersionUID = 1L;
97
98 private final Desire providedDesire;
99
100 public ProviderInjectionSatisfaction(Desire providedDesire) {
101 this.providedDesire = providedDesire;
102 }
103
104 @Override
105 public CachePolicy getDefaultCachePolicy() {
106 return CachePolicy.NO_PREFERENCE;
107 }
108
109 @Override
110 public List<Desire> getDependencies() {
111 return Collections.singletonList(providedDesire);
112 }
113
114 @Override
115 public Type getType() {
116 return Types.parameterizedType(Provider.class, providedDesire.getDesiredType());
117 }
118
119 @Override
120 public Class<?> getErasedType() {
121 return Provider.class;
122 }
123
124 @Override
125 public boolean hasInstance() {
126 return false;
127 }
128
129 @SuppressWarnings("unchecked")
130 @Override
131 public <T> T visit(SatisfactionVisitor<T> visitor) {
132 return (T) visitor.visitProviderClass((Class) InstanceProvider.class);
133 }
134
135 @Override
136 public Instantiator makeInstantiator(Map<Desire,Instantiator> dependencies,
137 LifecycleManager lm) {
138 Instantiator instantiator = dependencies.get(providedDesire);
139
140 // Inject an instance of a provider wrapping this instantiator.
141 return Instantiators.ofInstance(Instantiators.toProvider(instantiator));
142 }
143
144 @Override
145 public boolean equals(Object o) {
146 if (!(o instanceof ProviderInjectionSatisfaction)) {
147 return false;
148 }
149
150 return ((ProviderInjectionSatisfaction) o).providedDesire.equals(providedDesire);
151 }
152
153 @Override
154 public int hashCode() {
155 return providedDesire.hashCode();
156 }
157
158 @Override
159 public String toString() {
160 return "Provider<" + providedDesire + ">";
161 }
162 }
163 }