• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.type;
2 
3 import java.lang.reflect.*;
4 import java.util.*;
5 
6 import com.fasterxml.jackson.databind.JavaType;
7 import com.fasterxml.jackson.databind.util.ClassUtil;
8 
9 /**
10  * Helper class used for resolving type parameters for given class
11  */
12 public class TypeBindings
13 {
14     private final static JavaType[] NO_TYPES = new JavaType[0];
15 
16     /**
17      * Marker to use for (temporarily) unbound references.
18      */
19     public final static JavaType UNBOUND = new SimpleType(Object.class);
20 
21     /**
22      * Factory to use for constructing resolved related types.
23      */
24     protected final TypeFactory _typeFactory;
25 
26     /**
27      * @since 2.7
28      */
29     protected final ClassStack _classStack;
30 
31     /**
32      * Context type used for resolving all types, if specified. May be null,
33      * in which case {@link #_contextClass} is used instead.
34      */
35     protected final JavaType _contextType;
36 
37     /**
38      * Specific class to use for resolving all types, for methods and fields
39      * class and its superclasses and -interfaces contain.
40      */
41     protected final Class<?> _contextClass;
42 
43     /**
44      * Lazily-instantiated bindings of resolved type parameters
45      */
46     protected Map<String,JavaType> _bindings;
47 
48     /**
49      * Also: we may temporarily want to mark certain named types
50      * as resolved (but without exact type); if so, we'll just store
51      * names here.
52      */
53     protected HashSet<String> _placeholders;
54 
55     /**
56      * Sometimes it is necessary to allow hierarchic resolution of types: specifically
57      * in cases where there are local bindings (for methods, constructors). If so,
58      * we'll just use simple delegation model.
59      */
60     private final TypeBindings _parentBindings;
61 
62     /*
63     /**********************************************************
64     /* Construction
65     /**********************************************************
66      */
67 
TypeBindings(TypeFactory typeFactory, ClassStack stack, Class<?> cc)68     public TypeBindings(TypeFactory typeFactory, ClassStack stack, Class<?> cc)
69     {
70         this(typeFactory, null, stack, cc, null);
71     }
72 
TypeBindings(TypeFactory typeFactory, ClassStack stack, JavaType type)73     public TypeBindings(TypeFactory typeFactory, ClassStack stack, JavaType type)
74     {
75         this(typeFactory, null, stack, type.getRawClass(), type);
76     }
77 
78     /**
79      * Constructor used to create "child" instances; mostly to
80      * allow delegation from explicitly defined local overrides
81      * (local type variables for methods, constructors) to
82      * contextual (class-defined) ones.
83      */
childInstance()84     public TypeBindings childInstance() {
85         return new TypeBindings(_typeFactory, this, _classStack, _contextClass, _contextType);
86     }
87 
TypeBindings(TypeFactory tf, TypeBindings parent, ClassStack stack, Class<?> cc, JavaType type)88     private TypeBindings(TypeFactory tf, TypeBindings parent, ClassStack stack,
89             Class<?> cc, JavaType type)
90     {
91         _typeFactory = tf;
92         _parentBindings = parent;
93         _classStack = stack;
94         _contextClass = cc;
95         _contextType = type;
96     }
97 
98     /*
99     /**********************************************************
100     /* Pass-through type resolution methods
101     /**********************************************************
102      */
103 
resolveType(Class<?> cls)104     public JavaType resolveType(Class<?> cls) {
105         return _typeFactory._constructType(_classStack, cls, this);
106     }
107 
resolveType(Type type)108     public JavaType resolveType(Type type) {
109         return _typeFactory._constructType(_classStack, type, this);
110     }
111 
112     /*
113     /**********************************************************
114     /* Accesors
115     /**********************************************************
116      */
117 
findType(String name, boolean mustFind)118     public JavaType findType(String name, boolean mustFind)
119     {
120         if (_bindings == null) {
121             _resolve();
122         }
123         JavaType t = _bindings.get(name);
124         if (t != null) {
125             return t;
126         }
127         if (_placeholders != null && _placeholders.contains(name)) {
128             return UNBOUND;
129         }
130         if (_parentBindings != null) {
131             return _parentBindings.findType(name, mustFind);
132         }
133         // nothing found, so...
134         // Should we throw an exception or just return null?
135 
136         /* 18-Feb-2011, tatu: There are some tricky type bindings within
137          *   java.util, such as HashMap$KeySet; so let's punt the problem
138          *   (honestly not sure what to do -- they are unbound for good, I think)
139          */
140         if (_contextClass != null) {
141             if (ClassUtil.getEnclosingClass(_contextClass) != null) {
142                 // [JACKSON-572]: Actually, let's skip this for all non-static inner classes
143                 //   (which will also cover 'java.util' type cases...
144                 if (!Modifier.isStatic(_contextClass.getModifiers())) {
145                     return UNBOUND;
146                 }
147             }
148         }
149 
150         if (!mustFind) {
151             return null;
152         }
153 
154         String className;
155         if (_contextClass != null) {
156             className = _contextClass.getName();
157         } else if (_contextType != null) {
158             className = _contextType.toString();
159         } else {
160             className = "UNKNOWN";
161         }
162         throw new IllegalArgumentException("Type variable '"+name
163                 +"' can not be resolved (with context of class "+className+")");
164         //t = UNBOUND;
165     }
166 
addBinding(String name, JavaType type)167     public void addBinding(String name, JavaType type)
168     {
169         // note: emptyMap() is unmodifiable, hence second check is needed:
170         if (_bindings == null || _bindings.size() == 0) {
171             _bindings = new LinkedHashMap<String,JavaType>();
172         }
173         _bindings.put(name, type);
174     }
175 
typesAsArray()176     public JavaType[] typesAsArray()
177     {
178         if (_bindings == null) {
179             _resolve();
180         }
181         if (_bindings.size() == 0) {
182             return NO_TYPES;
183         }
184         return _bindings.values().toArray(new JavaType[_bindings.size()]);
185     }
186 
187     /*
188     /**********************************************************
189     /* Internal methods
190     /**********************************************************
191      */
192 
193     // Only for tests!
getBindingCount()194     protected int getBindingCount() {
195         if (_bindings == null) {
196             _resolve();
197         }
198         return _bindings.size();
199     }
200 
_resolve()201     protected void _resolve()
202     {
203         _resolveBindings(_contextClass);
204 
205         // finally: may have root level type info too
206         if (_contextType != null) {
207             int count = _contextType.containedTypeCount();
208             if (count > 0) {
209                 for (int i = 0; i < count; ++i) {
210                     String name = _contextType.containedTypeName(i);
211                     JavaType type = _contextType.containedType(i);
212                     addBinding(name, type);
213                 }
214             }
215         }
216 
217         // nothing bound? mark with empty map to prevent further calls
218         if (_bindings == null) {
219             _bindings = Collections.emptyMap();
220         }
221     }
222 
_addPlaceholder(String name)223     public void _addPlaceholder(String name) {
224         if (_placeholders == null) {
225             _placeholders = new HashSet<String>();
226         }
227         _placeholders.add(name);
228     }
229 
_resolveBindings(Type t)230     protected void _resolveBindings(Type t)
231     {
232         if (t == null) return;
233 
234         Class<?> raw;
235         if (t instanceof ParameterizedType) {
236             ParameterizedType pt = (ParameterizedType) t;
237             Type[] args = pt.getActualTypeArguments();
238             if (args  != null && args.length > 0) {
239                 Class<?> rawType = (Class<?>) pt.getRawType();
240                 TypeVariable<?>[] vars = rawType.getTypeParameters();
241                 if (vars.length != args.length) {
242                     throw new IllegalArgumentException("Strange parametrized type (in class "+rawType.getName()+"): number of type arguments != number of type parameters ("+args.length+" vs "+vars.length+")");
243                 }
244                 for (int i = 0, len = args.length; i < len; ++i) {
245                     TypeVariable<?> var = vars[i];
246                     String name = var.getName();
247                     if (_bindings == null) {
248                         _bindings = new LinkedHashMap<String,JavaType>();
249                     } else {
250                         // 24-Mar-2010, tatu: Better ensure that we do not overwrite something
251                         //  collected earlier (since we descend towards super-classes):
252                         if (_bindings.containsKey(name)) continue;
253                     }
254                     // first: add a placeholder to prevent infinite loops
255                     _addPlaceholder(name);
256                     // then resolve type
257                     _bindings.put(name, _typeFactory._constructType(_classStack, args[i], this));
258                 }
259             }
260             raw = (Class<?>)pt.getRawType();
261         } else if (t instanceof Class<?>) {
262             raw = (Class<?>) t;
263             /* [JACKSON-677]: If this is an inner class then the generics are defined on the
264              * enclosing class so we have to check there as well.  We don't
265              * need to call getEnclosingClass since anonymous classes declare
266              * generics
267              */
268             Class<?> decl = ClassUtil.getDeclaringClass(raw);
269             /* 08-Feb-2013, tatu: Except that if context is also super-class, we must
270              *   skip it; context will be checked anyway, and we'd get StackOverflow if
271              *   we went there.
272              */
273             if (decl != null && !decl.isAssignableFrom(raw)) {
274                 _resolveBindings(decl);
275             }
276 
277             /* 24-Mar-2010, tatu: Can not have true generics definitions, but can
278              *   have lower bounds ("<T extends BeanBase>") in declaration itself
279              */
280             TypeVariable<?>[] vars = raw.getTypeParameters();
281             if (vars != null && vars.length > 0) {
282                 JavaType[] typeParams = null;
283 
284                 if (_contextType != null && raw.isAssignableFrom(_contextType.getRawClass())) {
285                     typeParams = _typeFactory.findTypeParameters(_contextType, raw);
286                 }
287 
288                 for (int i = 0; i < vars.length; i++) {
289                     TypeVariable<?> var = vars[i];
290 
291                     String name = var.getName();
292                     Type varType = var.getBounds()[0];
293                     if (varType != null) {
294                         if (_bindings == null) {
295                             _bindings = new LinkedHashMap<String,JavaType>();
296                         } else { // and no overwriting...
297                             if (_bindings.containsKey(name)) continue;
298                         }
299                         _addPlaceholder(name); // to prevent infinite loops
300 
301                         if (typeParams != null && typeParams.length > i) {
302                             _bindings.put(name, typeParams[i]);
303                         } else {
304                             _bindings.put(name, _typeFactory._constructType(_classStack, varType, this));
305                         }
306                     }
307                 }
308             }
309         } else { // probably can't be any of these... so let's skip for now
310             //if (type instanceof GenericArrayType) {
311             //if (type instanceof TypeVariable<?>) {
312             // if (type instanceof WildcardType) {
313             return;
314         }
315         // but even if it's not a parameterized type, its super types may be:
316         _resolveBindings(ClassUtil.getGenericSuperclass(raw));
317         for (Type intType : raw.getGenericInterfaces()) {
318             _resolveBindings(intType);
319         }
320     }
321 
322     @Override
toString()323     public String toString()
324     {
325         if (_bindings == null) {
326             _resolve();
327         }
328         StringBuilder sb = new StringBuilder("[TypeBindings for ");
329         if (_contextType != null) {
330             sb.append(_contextType.toString());
331         } else {
332             sb.append(_contextClass.getName());
333         }
334         sb.append(": ").append(_bindings).append("]");
335         return sb.toString();
336     }
337 }
338