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