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
12 public class TypeBindings
13 implements java.io.Serializable
14 {
15 private static final long serialVersionUID = 1L;
16
17 private final static String[] NO_STRINGS = new String[0];
18
19 private final static JavaType[] NO_TYPES = new JavaType[0];
20
21 private final static TypeBindings EMPTY = new TypeBindings(NO_STRINGS, NO_TYPES, null);
22
23
24
25
26
27
30 private final String[] _names;
31
32
35 private final JavaType[] _types;
36
37
42 private final String[] _unboundVariables;
43
44 private final int _hashCode;
45
46
51
52 private TypeBindings(String[] names, JavaType[] types, String[] uvars)
53 {
54 _names = (names == null) ? NO_STRINGS : names;
55 _types = (types == null) ? NO_TYPES : types;
56 if (_names.length != _types.length) {
57 throw new IllegalArgumentException("Mismatching names ("+_names.length+"), types ("+_types.length+")");
58 }
59 int h = 1;
60 for (int i = 0, len = _types.length; i < len; ++i) {
61 h += _types[i].hashCode();
62 }
63 _unboundVariables = uvars;
64 _hashCode = h;
65 }
66
67 public static TypeBindings emptyBindings() {
68 return EMPTY;
69 }
70
71
72 protected Object readResolve() {
73 if ((_names == null) || (_names.length == 0)) {
74 return EMPTY;
75 }
76 return this;
77 }
78
79
83 public static TypeBindings create(Class<?> erasedType, List<JavaType> typeList)
84 {
85 JavaType[] types = (typeList == null || typeList.isEmpty()) ?
86 NO_TYPES : typeList.toArray(NO_TYPES);
87 return create(erasedType, types);
88 }
89
90 public static TypeBindings create(Class<?> erasedType, JavaType[] types)
91 {
92 if (types == null) {
93 types = NO_TYPES;
94 } else switch (types.length) {
95 case 1:
96 return create(erasedType, types[0]);
97 case 2:
98 return create(erasedType, types[0], types[1]);
99 }
100 TypeVariable<?>[] vars = erasedType.getTypeParameters();
101 String[] names;
102 if (vars == null || vars.length == 0) {
103 names = NO_STRINGS;
104 } else {
105 int len = vars.length;
106 names = new String[len];
107 for (int i = 0; i < len; ++i) {
108 names[i] = vars[i].getName();
109 }
110 }
111
112 if (names.length != types.length) {
113 throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
114 +" with "+types.length+" type parameter"
115 +((types.length == 1) ? "" : "s")+": class expects "+names.length);
116 }
117 return new TypeBindings(names, types, null);
118 }
119
120 public static TypeBindings create(Class<?> erasedType, JavaType typeArg1)
121 {
122
123 TypeVariable<?>[] vars = TypeParamStash.paramsFor1(erasedType);
124 int varLen = (vars == null) ? 0 : vars.length;
125 if (varLen != 1) {
126 throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
127 +" with 1 type parameter: class expects "+varLen);
128 }
129 return new TypeBindings(new String[] { vars[0].getName() },
130 new JavaType[] { typeArg1 }, null);
131 }
132
133 public static TypeBindings create(Class<?> erasedType, JavaType typeArg1, JavaType typeArg2)
134 {
135
136 TypeVariable<?>[] vars = TypeParamStash.paramsFor2(erasedType);
137 int varLen = (vars == null) ? 0 : vars.length;
138 if (varLen != 2) {
139 throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
140 +" with 2 type parameters: class expects "+varLen);
141 }
142 return new TypeBindings(new String[] { vars[0].getName(), vars[1].getName() },
143 new JavaType[] { typeArg1, typeArg2 }, null);
144 }
145
146
151 public static TypeBindings createIfNeeded(Class<?> erasedType, JavaType typeArg1)
152 {
153 TypeVariable<?>[] vars = erasedType.getTypeParameters();
154 int varLen = (vars == null) ? 0 : vars.length;
155 if (varLen == 0) {
156 return EMPTY;
157 }
158 if (varLen != 1) {
159 throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
160 +" with 1 type parameter: class expects "+varLen);
161 }
162 return new TypeBindings(new String[] { vars[0].getName() },
163 new JavaType[] { typeArg1 }, null);
164 }
165
166
171 public static TypeBindings createIfNeeded(Class<?> erasedType, JavaType[] types)
172 {
173 TypeVariable<?>[] vars = erasedType.getTypeParameters();
174 if (vars == null || vars.length == 0) {
175 return EMPTY;
176 }
177 if (types == null) {
178 types = NO_TYPES;
179 }
180 int len = vars.length;
181 String[] names = new String[len];
182 for (int i = 0; i < len; ++i) {
183 names[i] = vars[i].getName();
184 }
185
186 if (names.length != types.length) {
187 throw new IllegalArgumentException("Cannot create TypeBindings for class "+erasedType.getName()
188 +" with "+types.length+" type parameter"
189 +((types.length == 1) ? "" : "s")+": class expects "+names.length);
190 }
191 return new TypeBindings(names, types, null);
192 }
193
194
199 public TypeBindings withUnboundVariable(String name)
200 {
201 int len = (_unboundVariables == null) ? 0 : _unboundVariables.length;
202 String[] names = (len == 0)
203 ? new String[1] : Arrays.copyOf(_unboundVariables, len+1);
204 names[len] = name;
205 return new TypeBindings(_names, _types, names);
206 }
207
208
213
214
217 public JavaType findBoundType(String name)
218 {
219 for (int i = 0, len = _names.length; i < len; ++i) {
220 if (name.equals(_names[i])) {
221 JavaType t = _types[i];
222 if (t instanceof ResolvedRecursiveType) {
223 ResolvedRecursiveType rrt = (ResolvedRecursiveType) t;
224 JavaType t2 = rrt.getSelfReferencedType();
225 if (t2 != null) {
226 t = t2;
227 } else {
228
232
237 }
238 }
239 return t;
240 }
241 }
242 return null;
243 }
244
245 public boolean isEmpty() {
246 return (_types.length == 0);
247 }
248
249
252 public int size() {
253 return _types.length;
254 }
255
256 public String getBoundName(int index)
257 {
258 if (index < 0 || index >= _names.length) {
259 return null;
260 }
261 return _names[index];
262 }
263
264 public JavaType getBoundType(int index)
265 {
266 if (index < 0 || index >= _types.length) {
267 return null;
268 }
269 return _types[index];
270 }
271
272
275 public List<JavaType> getTypeParameters()
276 {
277 if (_types.length == 0) {
278 return Collections.emptyList();
279 }
280 return Arrays.asList(_types);
281 }
282
283
286 public boolean hasUnbound(String name) {
287 if (_unboundVariables != null) {
288 for (int i = _unboundVariables.length; --i >= 0; ) {
289 if (name.equals(_unboundVariables[i])) {
290 return true;
291 }
292 }
293 }
294 return false;
295 }
296
297
303 public Object asKey(Class<?> rawBase) {
304
305
306 return new AsKey(rawBase, _types, _hashCode);
307 }
308
309
314
315 @Override public String toString()
316 {
317 if (_types.length == 0) {
318 return "<>";
319 }
320 StringBuilder sb = new StringBuilder();
321 sb.append('<');
322 for (int i = 0, len = _types.length; i < len; ++i) {
323 if (i > 0) {
324 sb.append(',');
325 }
326
327 String sig = _types[i].getGenericSignature();
328 sb.append(sig);
329 }
330 sb.append('>');
331 return sb.toString();
332 }
333
334 @Override public int hashCode() { return _hashCode; }
335
336 @Override public boolean equals(Object o)
337 {
338 if (o == this) return true;
339 if (!ClassUtil.hasClass(o, getClass())) {
340 return false;
341 }
342 TypeBindings other = (TypeBindings) o;
343 int len = _types.length;
344 if (len != other.size()) {
345 return false;
346 }
347 JavaType[] otherTypes = other._types;
348 for (int i = 0; i < len; ++i) {
349 if (!otherTypes[i].equals(_types[i])) {
350 return false;
351 }
352 }
353 return true;
354 }
355
356
361
362 protected JavaType[] typeParameterArray() {
363 return _types;
364 }
365
366
371
372
373
374
375
376
383 static class TypeParamStash {
384 private final static TypeVariable<?>[] VARS_ABSTRACT_LIST = AbstractList.class.getTypeParameters();
385 private final static TypeVariable<?>[] VARS_COLLECTION = Collection.class.getTypeParameters();
386 private final static TypeVariable<?>[] VARS_ITERABLE = Iterable.class.getTypeParameters();
387 private final static TypeVariable<?>[] VARS_LIST = List.class.getTypeParameters();
388 private final static TypeVariable<?>[] VARS_ARRAY_LIST = ArrayList.class.getTypeParameters();
389
390 private final static TypeVariable<?>[] VARS_MAP = Map.class.getTypeParameters();
391 private final static TypeVariable<?>[] VARS_HASH_MAP = HashMap.class.getTypeParameters();
392 private final static TypeVariable<?>[] VARS_LINKED_HASH_MAP = LinkedHashMap.class.getTypeParameters();
393
394 public static TypeVariable<?>[] paramsFor1(Class<?> erasedType)
395 {
396 if (erasedType == Collection.class) {
397 return VARS_COLLECTION;
398 }
399 if (erasedType == List.class) {
400 return VARS_LIST;
401 }
402 if (erasedType == ArrayList.class) {
403 return VARS_ARRAY_LIST;
404 }
405 if (erasedType == AbstractList.class) {
406 return VARS_ABSTRACT_LIST;
407 }
408 if (erasedType == Iterable.class) {
409 return VARS_ITERABLE;
410 }
411 return erasedType.getTypeParameters();
412 }
413
414 public static TypeVariable<?>[] paramsFor2(Class<?> erasedType)
415 {
416 if (erasedType == Map.class) {
417 return VARS_MAP;
418 }
419 if (erasedType == HashMap.class) {
420 return VARS_HASH_MAP;
421 }
422 if (erasedType == LinkedHashMap.class) {
423 return VARS_LINKED_HASH_MAP;
424 }
425 return erasedType.getTypeParameters();
426 }
427 }
428
429
434 final static class AsKey {
435 private final Class<?> _raw;
436 private final JavaType[] _params;
437 private final int _hash;
438
439 public AsKey(Class<?> raw, JavaType[] params, int hash) {
440 _raw = raw ;
441 _params = params;
442 _hash = hash;
443 }
444
445 @Override
446 public int hashCode() { return _hash; }
447
448 @Override
449 public boolean equals(Object o) {
450 if (o == this) return true;
451 if (o == null) return false;
452 if (o.getClass() != getClass()) return false;
453 AsKey other = (AsKey) o;
454
455 if ((_hash == other._hash) && (_raw == other._raw)) {
456 final JavaType[] otherParams = other._params;
457 final int len = _params.length;
458
459 if (len == otherParams.length) {
460 for (int i = 0; i < len; ++i) {
461 if (!_params[i].equals(otherParams[i])) {
462 return false;
463 }
464 }
465 return true;
466 }
467 }
468 return false;
469 }
470
471 @Override
472 public String toString() {
473 return _raw.getName()+"<>";
474 }
475 }
476 }
477