1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  package org.grouplens.grapht;
21  
22  import org.apache.commons.lang3.ClassUtils;
23  import org.grouplens.grapht.BindingFunctionBuilder.RuleSet;
24  import org.grouplens.grapht.annotation.DefaultImplementation;
25  import org.grouplens.grapht.annotation.DefaultProvider;
26  import org.grouplens.grapht.context.ContextMatcher;
27  import org.grouplens.grapht.reflect.*;
28  import org.grouplens.grapht.solver.BindRuleBuilder;
29  import org.grouplens.grapht.solver.BindingFlag;
30  import org.grouplens.grapht.util.Preconditions;
31  import org.grouplens.grapht.util.Types;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import javax.annotation.Nonnull;
36  import javax.annotation.Nullable;
37  import javax.inject.Provider;
38  import java.lang.annotation.Annotation;
39  import java.math.BigDecimal;
40  import java.math.BigInteger;
41  import java.util.HashMap;
42  import java.util.HashSet;
43  import java.util.Map;
44  import java.util.Map.Entry;
45  import java.util.Set;
46  
47  
48  
49  
50  
51  
52  
53  
54  class BindingImpl<T> implements Binding<T> {
55      private static final Logger logger = LoggerFactory.getLogger(BindingImpl.class);
56      
57      private final ContextImpl context;
58      private final Class<T> sourceType;
59      
60      private final Set<Class<?>> excludeTypes;
61      
62      private final QualifierMatcher qualifier;
63  
64      private final CachePolicy cachePolicy;
65      private final boolean fixed;
66  
67      public BindingImpl(ContextImpl context, Class<T> type) {
68          this(context, type, context.getBuilder().getDefaultExclusions(),
69               Qualifiers.matchDefault(),
70               CachePolicy.NO_PREFERENCE,
71               false);
72      }
73  
74      public BindingImpl(ContextImpl context, Class<T> type,
75                         Set<Class<?>> excludes, QualifierMatcher matcher, 
76                         CachePolicy cachePolicy, boolean fixed) {
77          this.context = context;
78          this.cachePolicy = cachePolicy;
79          sourceType = type;
80          excludeTypes = excludes;
81          qualifier = matcher;
82          this.fixed = fixed;
83      }
84  
85      @Override
86      public Binding<T> withQualifier(@Nonnull Class<? extends Annotation> qualifier) {
87          QualifierMatcher q = Qualifiers.match(qualifier);
88          return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
89      }
90      
91      @Override
92      public Binding<T> withQualifier(@Nonnull Annotation annot) {
93          QualifierMatcher q = Qualifiers.match(annot);
94          return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
95      }
96  
97      @Override
98      public Binding<T> withAnyQualifier() {
99          QualifierMatcher q = Qualifiers.matchAny();
100         return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
101     }
102     
103     @Override
104     public Binding<T> unqualified() {
105         QualifierMatcher q = Qualifiers.matchNone();
106         return new BindingImpl<T>(context, sourceType, excludeTypes, q, cachePolicy, fixed);
107     }
108 
109     @Override
110     public Binding<T> exclude(@Nonnull Class<?> exclude) {
111         Preconditions.notNull("exclude type", exclude);
112         Set<Class<?>> excludes = new HashSet<Class<?>>(excludeTypes);
113         excludes.add(exclude);
114         return new BindingImpl<T>(context, sourceType, excludes, qualifier, cachePolicy, fixed);
115     }
116     
117     @Override
118     public Binding<T> shared() {
119         return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, CachePolicy.MEMOIZE, fixed);
120     }
121     
122     @Override
123     public Binding<T> unshared() {
124         return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, CachePolicy.NEW_INSTANCE, fixed);
125     }
126 
127     @Override
128     public Binding<T> fixed() {
129         return new BindingImpl<T>(context, sourceType, excludeTypes, qualifier, cachePolicy, true);
130     }
131 
132     @Override
133     public void to(@Nonnull Class<? extends T> impl, boolean chained) {
134         Preconditions.isAssignable(sourceType, impl);
135         if (logger.isWarnEnabled()) {
136             if (Types.shouldBeInstantiable(impl)
137                     && !Types.isInstantiable(impl)
138                     && impl.getAnnotation(DefaultProvider.class) == null
139                     && impl.getAnnotation(DefaultImplementation.class) == null) {
140                 logger.warn("Concrete type {} does not have an injectable or public default constructor, but probably should", impl);
141             }
142         }
143         
144         BindRuleBuilder brb = startRule();
145         if (Types.isInstantiable(impl)) {
146             brb.setSatisfaction(Satisfactions.type(impl));
147         } else {
148             brb.setImplementation(impl);
149         }
150         brb.setTerminal(!chained);
151         generateBindings(brb, impl);
152     }
153 
154     @Override
155     public void to(@Nonnull Class<? extends T> impl) {
156         to(impl, true);
157     }
158 
159     @Override
160     public void to(@Nullable T instance) {
161         if (instance == null) {
162             toNull();
163             return;
164         } else if (!(instance instanceof Number)
165                    && !ClassUtils.isPrimitiveWrapper(instance.getClass())
166                    && !sourceType.isInstance(instance)) {
167             String msg = String.format("%s is not an instance of %s",
168                                        instance, sourceType);
169             throw new InvalidBindingException(sourceType, msg);
170         }
171 
172         
173         Object coerced = coerce(instance);
174         Satisfaction s = Satisfactions.instance(coerced);
175         BindRuleBuilder brb = startRule().setSatisfaction(s);
176         generateBindings(brb, coerced.getClass());
177     }
178     
179     @Override
180     public void toProvider(@Nonnull Class<? extends Provider<? extends T>> provider) {
181         Satisfaction s = Satisfactions.providerType(provider);
182         BindRuleBuilder brb = startRule().setSatisfaction(s);
183         Class<?> provided;
184         try {
185             provided = Types.getProvidedType(provider);
186         } catch (IllegalArgumentException e) {
187             if (e.getMessage().endsWith("is generic")) {
188                 throw new InvalidBindingException(sourceType, "cannot bind to generic provider");
189             } else {
190                 throw e;
191             }
192         }
193         generateBindings(brb, provided);
194     }
195 
196     @Override
197     public void toProvider(@Nonnull Provider<? extends T> provider) {
198         Satisfaction s = Satisfactions.providerInstance(provider);
199         BindRuleBuilder brb = startRule().setSatisfaction(s);
200         Class<?> provided;
201         try {
202             provided = Types.getProvidedType(provider);
203         } catch (IllegalArgumentException e) {
204             if (e.getMessage().endsWith("is generic")) {
205                 throw new InvalidBindingException(sourceType, "cannot bind to generic provider");
206             } else {
207                 throw e;
208             }
209         }
210         generateBindings(brb, provided);
211     }
212 
213     @Override
214     public void toNull() {
215         toNull(sourceType);
216     }
217 
218     @Override
219     public void toNull(Class<? extends T> type) {
220         Satisfaction s = Satisfactions.nullOfType(type);
221         BindRuleBuilder brb = startRule().setSatisfaction(s);
222         generateBindings(brb, type);
223     }
224 
225     @Override
226     public void toSatisfaction(@Nonnull Satisfaction sat) {
227         Preconditions.notNull("satisfaction", sat);
228 
229         BindRuleBuilder brb = startRule().setSatisfaction(sat);
230         generateBindings(brb, sat.getErasedType());
231     }
232 
233     
234 
235 
236 
237 
238     private void generateBindings(BindRuleBuilder brb, Class<?> type) {
239         ContextMatcher matcher = context.getContextPattern();
240         BindingFunctionBuilder config = context.getBuilder();
241         if (config.getGenerateRules()) {
242             Map<Class<?>, RuleSet> bindPoints = generateBindPoints(type);
243             for (Entry<Class<?>, RuleSet> e: bindPoints.entrySet()) {
244                 config.addBindRule(e.getValue(), matcher, brb.setDependencyType(e.getKey()).build());
245             }
246         } else {
247             config.addBindRule(RuleSet.EXPLICIT, matcher, brb.setDependencyType(sourceType).build());
248         }
249     }
250 
251     
252 
253 
254 
255     private BindRuleBuilder startRule() {
256         BindRuleBuilder brb = new BindRuleBuilder();
257         brb.setQualifierMatcher(qualifier)
258            .setCachePolicy(cachePolicy)
259            .setTerminal(true);
260         if (fixed) {
261             brb.addFlag(BindingFlag.FIXED);
262         }
263         return brb;
264     }
265 
266     private Object coerce(Object in) {
267         Class<?> boxedSource = Types.box(sourceType);
268         if (Integer.class.equals(boxedSource)) {
269             
270             return Integer.valueOf(toBigInteger(in).intValue());
271         } else if (Short.class.equals(boxedSource)) {
272             
273             return Short.valueOf(toBigInteger(in).shortValue());
274         } else if (Byte.class.equals(boxedSource)) {
275             
276             return Byte.valueOf(toBigInteger(in).byteValue());
277         } else if (Long.class.equals(boxedSource)) {
278             
279             return Long.valueOf(toBigInteger(in).longValue());
280         } else if (Float.class.equals(boxedSource)) {
281             
282             return Float.valueOf(toBigDecimal(in).floatValue());
283         } else if (Double.class.equals(boxedSource)) {
284             
285             return Double.valueOf(toBigDecimal(in).doubleValue());
286         } else if (BigDecimal.class.equals(boxedSource)) {
287             
288             return toBigDecimal(in);
289         } else if (BigInteger.class.equals(boxedSource)) {
290             
291             return toBigInteger(in);
292         } else {
293             
294             return in;
295         }
296     }
297     
298     private BigDecimal toBigDecimal(Object in) {
299         
300         
301         return new BigDecimal(in.toString());
302     }
303     
304     private BigInteger toBigInteger(Object in) {
305         
306         
307         
308         return new BigInteger(in.toString());
309     }
310     
311     private Map<Class<?>, RuleSet> generateBindPoints(Class<?> target) {
312         Map<Class<?>, RuleSet> bindPoints = new HashMap<Class<?>, RuleSet>();
313         
314         recordTypes(Types.box(sourceType), target, bindPoints);
315         return bindPoints;
316     }
317     
318     private void recordTypes(Class<?> src, Class<?> type, Map<Class<?>, RuleSet> bindPoints) {
319         
320         if (type == null || excludeTypes.contains(type)) {
321             
322             
323             return;
324         }
325         
326         RuleSet set;
327         if (type.equals(src)) {
328             
329             set = RuleSet.EXPLICIT;
330         } else if (src.isAssignableFrom(type)) {
331             
332             
333             set = RuleSet.INTERMEDIATE_TYPES;
334         } else if (type.isAssignableFrom(src)) {
335             
336             
337             set = RuleSet.SUPER_TYPES;
338         } else {
339             
340             
341             return;
342         }
343         
344         
345         bindPoints.put(type, set);
346         
347         
348         
349         
350         
351         recordTypes(src, type.getSuperclass(), bindPoints);
352         for (Class<?> i: type.getInterfaces()) {
353             recordTypes(src, i, bindPoints);
354         }
355     }
356 }