TypeDescriptor.java 28.5 KB
Newer Older
K
polish  
Keith Donald 已提交
1
/*
2
 * Copyright 2002-2013 the original author or authors.
3
 *
K
polish  
Keith Donald 已提交
4 5 6
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
7
 *
K
polish  
Keith Donald 已提交
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
K
polish  
Keith Donald 已提交
10 11 12 13 14 15
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
16

K
Keith Donald 已提交
17 18
package org.springframework.core.convert;

P
Phillip Webb 已提交
19
import java.io.Serializable;
K
Keith Donald 已提交
20 21 22
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
23
import java.util.HashMap;
K
polish  
Keith Donald 已提交
24
import java.util.Map;
K
Keith Donald 已提交
25 26

import org.springframework.core.MethodParameter;
27
import org.springframework.core.ResolvableType;
P
Phillip Webb 已提交
28
import org.springframework.util.Assert;
29
import org.springframework.util.ClassUtils;
K
Keith Donald 已提交
30
import org.springframework.util.ObjectUtils;
K
Keith Donald 已提交
31 32

/**
K
Keith Donald 已提交
33
 * Context about a type to convert from or to.
J
Juergen Hoeller 已提交
34
 *
K
Keith Donald 已提交
35
 * @author Keith Donald
36
 * @author Andy Clement
J
Juergen Hoeller 已提交
37
 * @author Juergen Hoeller
38
 * @author Phillip Webb
S
Sam Brannen 已提交
39
 * @author Sam Brannen
40
 * @since 3.0
K
Keith Donald 已提交
41
 */
P
Phillip Webb 已提交
42 43 44 45
public class TypeDescriptor implements Serializable {

	private static final long serialVersionUID = 1L;

K
Keith Donald 已提交
46

47 48
	static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];

49
	private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<Class<?>, TypeDescriptor>();
50

51 52 53 54
	private static final Class<?>[] CACHED_COMMON_TYPES = { Boolean.class, byte.class,
		Byte.class, char.class, Character.class, short.class, Short.class, int.class,
		Integer.class, long.class, Long.class, float.class, Float.class, double.class,
		Double.class, String.class };
55
	static {
56 57 58
		for (Class<?> preCachedClass : CACHED_COMMON_TYPES) {
			commonTypesCache.put(preCachedClass, valueOf(preCachedClass));
		}
59
	}
J
Juergen Hoeller 已提交
60

61

62
	private final Class<?> type;
J
Juergen Hoeller 已提交
63

64
	private final ResolvableType resolvableType;
65

66
	private final Annotation[] annotations;
J
Juergen Hoeller 已提交
67

68

K
Keith Donald 已提交
69
	/**
K
Keith Donald 已提交
70
	 * Create a new type descriptor from a {@link MethodParameter}.
S
Sam Brannen 已提交
71 72
	 * <p>Use this constructor when a source or target conversion point is a
	 * constructor parameter, method parameter, or method return value.
73
	 * @param methodParameter the method parameter
K
Keith Donald 已提交
74
	 */
75
	public TypeDescriptor(MethodParameter methodParameter) {
76 77 78 79 80 81 82 83 84
		Assert.notNull(methodParameter, "MethodParameter must not be null");
		if (methodParameter.getNestingLevel() != 1) {
			throw new IllegalArgumentException("MethodParameter argument must have its nestingLevel set to 1");
		}
		this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
		this.type = this.resolvableType.resolve(Object.class);
		this.annotations = (methodParameter.getParameterIndex() == -1 ?
				nullSafeAnnotations(methodParameter.getMethodAnnotations()) :
				nullSafeAnnotations(methodParameter.getParameterAnnotations()));
K
Keith Donald 已提交
85 86
	}

K
Keith Donald 已提交
87
	/**
88
	 * Create a new type descriptor from a {@link Field}.
S
Sam Brannen 已提交
89
	 * <p>Use this constructor when a source or target conversion point is a field.
90
	 * @param field the field
K
Keith Donald 已提交
91 92
	 */
	public TypeDescriptor(Field field) {
93 94 95 96
		Assert.notNull(field, "Field must not be null");
		this.resolvableType = ResolvableType.forField(field);
		this.type = this.resolvableType.resolve(Object.class);
		this.annotations = nullSafeAnnotations(field.getAnnotations());
97 98 99
	}

	/**
100
	 * Create a new type descriptor from a {@link Property}.
S
Sam Brannen 已提交
101 102
	 * <p>Use this constructor when a source or target conversion point is a
	 * property on a Java class.
K
Keith Donald 已提交
103
	 * @param property the property
104
	 */
K
Keith Donald 已提交
105
	public TypeDescriptor(Property property) {
106 107 108 109
		Assert.notNull(property, "Property must not be null");
		this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter());
		this.type = this.resolvableType.resolve(Object.class);
		this.annotations = nullSafeAnnotations(property.getAnnotations());
K
Keith Donald 已提交
110
	}
111

112 113 114 115
	private TypeDescriptor(ResolvableType resolvableType, Class<?> type, Annotation[] annotations) {
		this.resolvableType = resolvableType;
		this.type = (type != null ? type : resolvableType.resolve(Object.class));
		this.annotations = nullSafeAnnotations(annotations);
116
	}
117 118


119 120
	private Annotation[] nullSafeAnnotations(Annotation[] annotations) {
		return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY;
121
	}
122

K
Keith Donald 已提交
123
	/**
124 125 126
	 * Variation of {@link #getType()} that accounts for a primitive type by returning its object wrapper type.
	 * <p>This is useful for conversion service implementations that wish to normalize to object-based types
	 * and not work with primitive types directly.
K
Keith Donald 已提交
127
	 */
128 129
	public Class<?> getObjectType() {
		return ClassUtils.resolvePrimitiveIfNecessary(getType());
130
	}
K
Keith Donald 已提交
131

132
	/**
133
	 * The type of the backing class, method parameter, field, or property described by this TypeDescriptor.
S
Sam Brannen 已提交
134
	 * <p>Returns primitive types as-is.
135 136
	 * <p>See {@link #getObjectType()} for a variation of this operation that resolves primitive types
	 * to their corresponding Object types if necessary.
137
	 * @return the type, or {@code null}
138
	 * @see #getObjectType()
139
	 */
140
	public Class<?> getType() {
J
Juergen Hoeller 已提交
141
		return this.type;
142 143
	}

K
Keith Donald 已提交
144 145
	/**
	 * Narrows this {@link TypeDescriptor} by setting its type to the class of the provided value.
S
Sam Brannen 已提交
146
	 * <p>If the value is {@code null}, no narrowing is performed and this TypeDescriptor is returned unchanged.
147
	 * <p>Designed to be called by binding frameworks when they read property, field, or method return values.
K
Keith Donald 已提交
148
	 * Allows such frameworks to narrow a TypeDescriptor built from a declared property, field, or method return value type.
149 150
	 * For example, a field declared as {@code java.lang.Object} would be narrowed to {@code java.util.HashMap}
	 * if it was set to a {@code java.util.HashMap} value. The narrowed TypeDescriptor can then be used to convert
151
	 * the HashMap to some other type. Annotation and nested type context is preserved by the narrowed copy.
K
Keith Donald 已提交
152 153 154 155
	 * @param value the value to use for narrowing this type descriptor
	 * @return this TypeDescriptor narrowed (returns a copy with its type updated to the class of the provided value)
	 */
	public TypeDescriptor narrow(Object value) {
156 157 158
		if (value == null) {
			return this;
		}
159 160
		ResolvableType narrowed = ResolvableType.forType(value.getClass(), this.resolvableType);
		return new TypeDescriptor(narrowed, null, this.annotations);
161
	}
K
Keith Donald 已提交
162

P
Phillip Webb 已提交
163 164 165 166 167 168
	/**
	 * Cast this {@link TypeDescriptor} to a superclass or implemented interface
	 * preserving annotations and nested type context.
	 * @param superType the super type to cast to (can be {@code null}
	 * @return a new TypeDescriptor for the up-cast type
	 * @throws IllegalArgumentException if this type is not assignable to the super-type
C
Chris Beams 已提交
169
	 * @since 3.2
P
Phillip Webb 已提交
170 171 172 173 174 175
	 */
	public TypeDescriptor upcast(Class<?> superType) {
		if (superType == null) {
			return null;
		}
		Assert.isAssignable(superType, getType());
176
		return new TypeDescriptor(this.resolvableType.as(superType), superType, this.annotations);
P
Phillip Webb 已提交
177 178
	}

179
	/**
180
	 * Returns the name of this type: the fully qualified class name.
181
	 */
182
	public String getName() {
183
		return ClassUtils.getQualifiedName(getType());
184
	}
J
Juergen Hoeller 已提交
185

186
	/**
187
	 * Is this type a primitive type?
188
	 */
189
	public boolean isPrimitive() {
190
		return getType().isPrimitive();
191
	}
K
Keith Donald 已提交
192

193
	/**
K
javadoc  
Keith Donald 已提交
194
	 * The annotations associated with this type descriptor, if any.
P
Phillip Webb 已提交
195
	 * @return the annotations, or an empty array if none
196
	 */
197
	public Annotation[] getAnnotations() {
198
		return this.annotations;
199 200
	}

201 202 203 204 205 206 207 208 209
	/**
	 * Determine if this type descriptor has the specified annotation.
	 * @param annotationType the annotation type
	 * @return <tt>true</tt> if the annotation is present
	 */
	public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
		return getAnnotation(annotationType) != null;
	}

210
	/**
K
javadoc  
Keith Donald 已提交
211
	 * Obtain the annotation associated with this type descriptor of the specified type.
P
Phillip Webb 已提交
212
	 * @param annotationType the annotation type
213
	 * @return the annotation, or {@code null} if no such annotation exists on this type descriptor
214
	 */
215 216
	@SuppressWarnings("unchecked")
	public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
217
		for (Annotation annotation : getAnnotations()) {
218
			if (annotation.annotationType().equals(annotationType)) {
219
				return (T) annotation;
220 221
			}
		}
222
		for (Annotation metaAnn : getAnnotations()) {
223 224 225 226 227
			T ann = metaAnn.annotationType().getAnnotation(annotationType);
			if (ann != null) {
				return ann;
			}
		}
228
		return null;
229 230
	}

K
Keith Donald 已提交
231
	/**
232
	 * Returns true if an object of this type descriptor can be assigned to the location described by the given type descriptor.
S
Sam Brannen 已提交
233
	 * <p>For example, valueOf(String.class).isAssignableTo(valueOf(CharSequence.class)) returns true because a String value can be assigned to a CharSequence variable.
234 235 236 237
	 * On the other hand, valueOf(Number.class).isAssignableTo(valueOf(Integer.class)) returns false because, while all Integers are Numbers, not all Numbers are Integers.
	 * <p>
	 * For arrays, collections, and maps, element and key/value types are checked if declared.
	 * For example, a List&lt;String&gt; field value is assignable to a Collection&lt;CharSequence&gt; field, but List&lt;Number&gt; is not assignable to List&lt;Integer&gt;.
P
Phillip Webb 已提交
238
	 * @return true if this type is assignable to the type represented by the provided type descriptor
239
	 * @see #getObjectType()
K
Keith Donald 已提交
240
	 */
241
	public boolean isAssignableTo(TypeDescriptor typeDescriptor) {
242 243 244 245
		boolean typesAssignable = typeDescriptor.getObjectType().isAssignableFrom(getObjectType());
		if (!typesAssignable) {
			return false;
		}
246
		if (isArray() && typeDescriptor.isArray()) {
247
			return getElementTypeDescriptor().isAssignableTo(typeDescriptor.getElementTypeDescriptor());
J
Juergen Hoeller 已提交
248 249
		}
		else if (isCollection() && typeDescriptor.isCollection()) {
250
			return isNestedAssignable(getElementTypeDescriptor(), typeDescriptor.getElementTypeDescriptor());
J
Juergen Hoeller 已提交
251 252
		}
		else if (isMap() && typeDescriptor.isMap()) {
253 254
			return isNestedAssignable(getMapKeyTypeDescriptor(), typeDescriptor.getMapKeyTypeDescriptor()) &&
				isNestedAssignable(getMapValueTypeDescriptor(), typeDescriptor.getMapValueTypeDescriptor());
J
Juergen Hoeller 已提交
255 256
		}
		else {
257 258
			return true;
		}
K
Keith Donald 已提交
259 260
	}

261 262 263 264 265 266
	private boolean isNestedAssignable(TypeDescriptor nestedTypeDescriptor, TypeDescriptor otherNestedTypeDescriptor) {
		if (nestedTypeDescriptor == null || otherNestedTypeDescriptor == null) {
			return true;
		}
		return nestedTypeDescriptor.isAssignableTo(otherNestedTypeDescriptor);
	}
K
Keith Donald 已提交
267

K
polish  
Keith Donald 已提交
268
	/**
K
javadoc  
Keith Donald 已提交
269
	 * Is this type a {@link Collection} type?
K
polish  
Keith Donald 已提交
270
	 */
K
javadoc  
Keith Donald 已提交
271
	public boolean isCollection() {
272
		return Collection.class.isAssignableFrom(getType());
K
Keith Donald 已提交
273 274
	}

K
polish  
Keith Donald 已提交
275
	/**
K
javadoc  
Keith Donald 已提交
276
	 * Is this type an array type?
K
polish  
Keith Donald 已提交
277
	 */
K
javadoc  
Keith Donald 已提交
278
	public boolean isArray() {
279
		return getType().isArray();
K
Keith Donald 已提交
280
	}
K
Keith Donald 已提交
281

K
Keith Donald 已提交
282
	/**
283 284 285
	 * If this type is an array, returns the array's component type.
	 * If this type is a {@link Collection} and it is parameterized, returns the Collection's element type.
	 * If the Collection is not parameterized, returns null indicating the element type is not declared.
286
	 * @return the array component type or Collection element type, or {@code null} if this type is a Collection but its element type is not parameterized
287
	 * @throws IllegalStateException if this type is not a java.util.Collection or Array type
K
Keith Donald 已提交
288
	 */
289
	public TypeDescriptor getElementTypeDescriptor() {
K
Keith Donald 已提交
290
		assertCollectionOrArray();
291 292 293 294 295
		if (this.resolvableType.isArray()) {
			return getRelatedIfResolvable(this, this.resolvableType.getComponentType());
		}
		return getRelatedIfResolvable(this, this.resolvableType.asCollection().getGeneric());

296 297
	}

K
Keith Donald 已提交
298
	/**
299
	 * If this type is a {@link Collection} or an Array, creates a element TypeDescriptor from the provided collection or array element.
S
Sam Brannen 已提交
300
	 * <p>Narrows the {@link #getElementTypeDescriptor() elementType} property to the class of the provided collection or array element.
K
Keith Donald 已提交
301 302
	 * For example, if this describes a java.util.List&lt;java.lang.Number&lt; and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
	 * If this describes a java.util.List&lt;?&gt; and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well.
S
Sam Brannen 已提交
303
	 * <p>Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned.
K
Keith Donald 已提交
304
	 * @param element the collection or array element
K
Keith Donald 已提交
305 306 307
	 * @return a element type descriptor, narrowed to the type of the provided element
	 * @throws IllegalStateException if this type is not a java.util.Collection or Array type
	 * @see #narrow(Object)
K
Keith Donald 已提交
308
	 */
309 310
	public TypeDescriptor elementTypeDescriptor(Object element) {
		return narrow(element, getElementTypeDescriptor());
311 312
	}

K
Keith Donald 已提交
313 314 315 316
	/**
	 * Is this type a {@link Map} type?
	 */
	public boolean isMap() {
317
		return Map.class.isAssignableFrom(getType());
K
Keith Donald 已提交
318
	}
319

K
Keith Donald 已提交
320
	/**
321 322
	 * If this type is a {@link Map} and its key type is parameterized, returns the map's key type.
	 * If the Map's key type is not parameterized, returns null indicating the key type is not declared.
323
	 * @return the Map key type, or {@code null} if this type is a Map but its key type is not parameterized
P
Phillip Webb 已提交
324
	 * @throws IllegalStateException if this type is not a java.util.Map
K
Keith Donald 已提交
325
	 */
326
	public TypeDescriptor getMapKeyTypeDescriptor() {
K
Keith Donald 已提交
327
		assertMap();
328
		return getRelatedIfResolvable(this, this.resolvableType.asMap().getGeneric(0));
K
Keith Donald 已提交
329 330
	}

K
Keith Donald 已提交
331
	/**
332
	 * If this type is a {@link Map}, creates a mapKey {@link TypeDescriptor} from the provided map key.
S
Sam Brannen 已提交
333
	 * <p>Narrows the {@link #getMapKeyTypeDescriptor() mapKeyType} property to the class of the provided map key.
K
Keith Donald 已提交
334
	 * For example, if this describes a java.util.Map&lt;java.lang.Number, java.lang.String&lt; and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
S
Sam Brannen 已提交
335 336
	 * <p>If this describes a java.util.Map&lt;?, ?&gt; and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well.
	 * <p>Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned.
K
Keith Donald 已提交
337 338
	 * @param mapKey the map key
	 * @return the map key type descriptor
P
Phillip Webb 已提交
339
	 * @throws IllegalStateException if this type is not a java.util.Map
K
Keith Donald 已提交
340
	 * @see #narrow(Object)
K
Keith Donald 已提交
341
	 */
342
	public TypeDescriptor getMapKeyTypeDescriptor(Object mapKey) {
343
		return narrow(mapKey, getMapKeyTypeDescriptor());
344
	}
345

K
polish  
Keith Donald 已提交
346
	/**
347
	 * If this type is a {@link Map} and its value type is parameterized, returns the map's value type.
S
Sam Brannen 已提交
348
	 * <p>If the Map's value type is not parameterized, returns null indicating the value type is not declared.
349
	 * @return the Map value type, or {@code null} if this type is a Map but its value type is not parameterized
P
Phillip Webb 已提交
350
	 * @throws IllegalStateException if this type is not a java.util.Map
K
polish  
Keith Donald 已提交
351
	 */
352
	public TypeDescriptor getMapValueTypeDescriptor() {
K
Keith Donald 已提交
353
		assertMap();
354
		return getRelatedIfResolvable(this, this.resolvableType.asMap().getGeneric(1));
K
Keith Donald 已提交
355
	}
356

K
Keith Donald 已提交
357
	/**
358
	 * If this type is a {@link Map}, creates a mapValue {@link TypeDescriptor} from the provided map value.
S
Sam Brannen 已提交
359
	 * <p>Narrows the {@link #getMapValueTypeDescriptor() mapValueType} property to the class of the provided map value.
K
Keith Donald 已提交
360 361
	 * For example, if this describes a java.util.Map&lt;java.lang.String, java.lang.Number&lt; and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
	 * If this describes a java.util.Map&lt;?, ?&gt; and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well.
S
Sam Brannen 已提交
362
	 * <p>Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned.
K
Keith Donald 已提交
363 364
	 * @param mapValue the map value
	 * @return the map value type descriptor
P
Phillip Webb 已提交
365
	 * @throws IllegalStateException if this type is not a java.util.Map
K
Keith Donald 已提交
366
	 */
367
	public TypeDescriptor getMapValueTypeDescriptor(Object mapValue) {
P
Phillip Webb 已提交
368
		return narrow(mapValue, getMapValueTypeDescriptor());
369
	}
370

371 372
	/**
	 * Returns the value of {@link TypeDescriptor#getType() getType()} for the {@link #getElementTypeDescriptor() elementTypeDescriptor}.
P
Phillip Webb 已提交
373
	 * @deprecated in Spring 3.1 in favor of {@link #getElementTypeDescriptor()}
374 375 376 377
	 * @throws IllegalStateException if this type is not a java.util.Collection or Array type
	 */
	@Deprecated
	public Class<?> getElementType() {
378
		return getType(getElementTypeDescriptor());
379 380 381
	}

	/**
382
	 * Returns the value of {@link TypeDescriptor#getType() getType()} for the {@link #getMapKeyTypeDescriptor() getMapKeyTypeDescriptor}.
P
Phillip Webb 已提交
383 384
	 * @deprecated in Spring 3.1 in favor of {@link #getMapKeyTypeDescriptor()}
	 * @throws IllegalStateException if this type is not a java.util.Map
385 386 387
	 */
	@Deprecated
	public Class<?> getMapKeyType() {
388
		return getType(getMapKeyTypeDescriptor());
389 390 391
	}

	/**
392
	 * Returns the value of {@link TypeDescriptor#getType() getType()} for the {@link #getMapValueTypeDescriptor() getMapValueTypeDescriptor}.
P
Phillip Webb 已提交
393 394
	 * @deprecated in Spring 3.1 in favor of {@link #getMapValueTypeDescriptor()}
	 * @throws IllegalStateException if this type is not a java.util.Map
395 396 397
	 */
	@Deprecated
	public Class<?> getMapValueType() {
398
		return getType(getMapValueTypeDescriptor());
K
Keith Donald 已提交
399
	}
400

401 402
	private Class<?> getType(TypeDescriptor typeDescriptor) {
		return (typeDescriptor == null ? null : typeDescriptor.getType());
403 404
	}

K
Keith Donald 已提交
405 406 407
	private void assertCollectionOrArray() {
		if (!isCollection() && !isArray()) {
			throw new IllegalStateException("Not a java.util.Collection or Array");
K
Keith Donald 已提交
408
		}
K
Keith Donald 已提交
409
	}
K
Keith Donald 已提交
410

K
Keith Donald 已提交
411 412 413
	private void assertMap() {
		if (!isMap()) {
			throw new IllegalStateException("Not a java.util.Map");
K
Keith Donald 已提交
414
		}
K
Keith Donald 已提交
415
	}
K
Keith Donald 已提交
416

417 418 419
	private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) {
		if (typeDescriptor != null) {
			return typeDescriptor.narrow(value);
J
Juergen Hoeller 已提交
420
		}
421
		return (value != null ? new TypeDescriptor(this.resolvableType, value.getClass(), this.annotations) : null);
422 423
	}

424
	@Override
425 426 427 428 429 430 431 432
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof TypeDescriptor)) {
			return false;
		}
		TypeDescriptor other = (TypeDescriptor) obj;
433
		if (!ObjectUtils.nullSafeEquals(this.type, other.type)) {
434 435
			return false;
		}
436
		if (getAnnotations().length != other.getAnnotations().length) {
437 438
			return false;
		}
439
		for (Annotation ann : this.getAnnotations()) {
440
			if (other.getAnnotation(ann.annotationType()) == null) {
441 442 443
				return false;
			}
		}
444
		if (isCollection() || isArray()) {
445
			return ObjectUtils.nullSafeEquals(this.getElementTypeDescriptor(), other.getElementTypeDescriptor());
446 447
		}
		else if (isMap()) {
448 449
			return ObjectUtils.nullSafeEquals(this.getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) &&
					ObjectUtils.nullSafeEquals(this.getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor());
450 451 452 453 454 455
		}
		else {
			return true;
		}
	}

456
	@Override
457 458 459 460
	public int hashCode() {
		return getType().hashCode();
	}

461
	@Override
462 463
	public String toString() {
		StringBuilder builder = new StringBuilder();
464
		for (Annotation ann : getAnnotations()) {
465 466
			builder.append("@").append(ann.annotationType().getName()).append(' ');
		}
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
		builder.append(this.resolvableType.toString());
		return builder.toString();
	}

	/**
	 * Create a new type descriptor from the given type.
	 * <p>Use this to instruct the conversion system to convert an object to a
	 * specific target type, when no type location such as a method parameter or
	 * field is available to provide additional conversion context.
	 * <p>Generally prefer use of {@link #forObject(Object)} for constructing type
	 * descriptors from source objects, as it handles the {@code null} object case.
	 * @param type the class
	 * @return the type descriptor
	 */
	public static TypeDescriptor valueOf(Class<?> type) {
		Assert.notNull(type, "Type must not be null");
		TypeDescriptor desc = commonTypesCache.get(type);
		return (desc != null ? desc : new TypeDescriptor(ResolvableType.forClass(type), null, null));
	}

	/**
	 * Create a new type descriptor from a {@link java.util.Collection} type.
	 * <p>Useful for converting to typed Collections.
	 * <p>For example, a {@code List<String>} could be converted to a
	 * {@code List<EmailAddress>} by converting to a targetType built with this method.
	 * The method call to construct such a {@code TypeDescriptor} would look something
	 * like: {@code collection(List.class, TypeDescriptor.valueOf(EmailAddress.class));}
	 * @param collectionType the collection type, which must implement {@link Collection}.
	 * @param elementTypeDescriptor a descriptor for the collection's element type,
	 * used to convert collection elements
	 * @return the collection type descriptor
	 */
	public static TypeDescriptor collection(Class<?> collectionType, TypeDescriptor elementTypeDescriptor) {
		Assert.notNull(collectionType, "CollectionType must not be null");
		if (!Collection.class.isAssignableFrom(collectionType)) {
			throw new IllegalArgumentException("collectionType must be a java.util.Collection");
503
		}
504 505
		ResolvableType element = (elementTypeDescriptor == null ? null
				: elementTypeDescriptor.resolvableType);
506
		return new TypeDescriptor(ResolvableType.forClassWithGenerics(collectionType,
507
				element), null, null);
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
	}

	/**
	 * Create a new type descriptor from a {@link java.util.Map} type.
	 * <p>Useful for converting to typed Maps.
	 * <p>For example, a Map&lt;String, String&gt; could be converted to a Map&lt;Id, EmailAddress&gt; by converting to a targetType built with this method:
	 * The method call to construct such a TypeDescriptor would look something like: map(Map.class, TypeDescriptor.valueOf(Id.class), TypeDescriptor.valueOf(EmailAddress.class));
	 * @param mapType the map type, which must implement {@link Map}
	 * @param keyTypeDescriptor a descriptor for the map's key type, used to convert map keys
	 * @param valueTypeDescriptor the map's value type, used to convert map values
	 * @return the map type descriptor
	 */
	public static TypeDescriptor map(Class<?> mapType, TypeDescriptor keyTypeDescriptor, TypeDescriptor valueTypeDescriptor) {
		if (!Map.class.isAssignableFrom(mapType)) {
			throw new IllegalArgumentException("mapType must be a java.util.Map");
523
		}
524 525 526
		ResolvableType key = (keyTypeDescriptor == null ? null : keyTypeDescriptor.resolvableType);
		ResolvableType value = (valueTypeDescriptor == null ? null : valueTypeDescriptor.resolvableType);
		return new TypeDescriptor(ResolvableType.forClassWithGenerics(mapType, key, value), null, null);
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
	}

	/**
	 * Create a new type descriptor as an array of the specified type.
	 * <p>For example to create a {@code Map<String,String>[]} use
	 * {@code TypeDescriptor.array(TypeDescriptor.map(Map.class, TypeDescriptor.value(String.class), TypeDescriptor.value(String.class)))}.
	 * @param elementTypeDescriptor the {@link TypeDescriptor} of the array element or {@code null}
	 * @return an array {@link TypeDescriptor} or {@code null} if {@code elementTypeDescriptor} is {@code null}
	 * @since 3.2.1
	 */
	public static TypeDescriptor array(TypeDescriptor elementTypeDescriptor) {
		if(elementTypeDescriptor == null) {
			return null;
		}
		return new TypeDescriptor(
				ResolvableType.forArrayComponent(elementTypeDescriptor.resolvableType),
				null, elementTypeDescriptor.getAnnotations());
	}

	/**
	 * Creates a type descriptor for a nested type declared within the method parameter.
	 * <p>For example, if the methodParameter is a {@code List<String>} and the
	 * nesting level is 1, the nested type descriptor will be String.class.
	 * <p>If the methodParameter is a {@code List<List<String>>} and the nesting
	 * level is 2, the nested type descriptor will also be a String.class.
	 * <p>If the methodParameter is a {@code Map<Integer, String>} and the nesting
	 * level is 1, the nested type descriptor will be String, derived from the map value.
	 * <p>If the methodParameter is a {@code List<Map<Integer, String>>} and the
	 * nesting level is 2, the nested type descriptor will be String, derived from the map value.
	 * <p>Returns {@code null} if a nested type cannot be obtained because it was not declared.
	 * For example, if the method parameter is a {@code List<?>}, the nested type
	 * descriptor returned will be {@code null}.
	 * @param methodParameter the method parameter with a nestingLevel of 1
	 * @param nestingLevel the nesting level of the collection/array element or
	 * map key/value declaration within the method parameter
	 * @return the nested type descriptor at the specified nesting level, or null
	 * if it could not be obtained
	 * @throws IllegalArgumentException if the nesting level of the input
	 * {@link MethodParameter} argument is not 1
	 * @throws IllegalArgumentException if the types up to the specified nesting
	 * level are not of collection, array, or map types
	 */
	public static TypeDescriptor nested(MethodParameter methodParameter, int nestingLevel) {
		if (methodParameter.getNestingLevel() != 1) {
			throw new IllegalArgumentException("methodParameter nesting level must be 1: use the nestingLevel parameter to specify the desired nestingLevel for nested type traversal");
		}
		return nested(new TypeDescriptor(methodParameter), nestingLevel);
	}

	/**
	 * Creates a type descriptor for a nested type declared within the field.
	 * <p>For example, if the field is a {@code List<String>} and the nesting
	 * level is 1, the nested type descriptor will be {@code String.class}.
	 * <p>If the field is a {@code List<List<String>>} and the nesting level is
	 * 2, the nested type descriptor will also be a {@code String.class}.
	 * <p>If the field is a {@code Map<Integer, String>} and the nesting level
	 * is 1, the nested type descriptor will be String, derived from the map value.
	 * <p>If the field is a {@code List<Map<Integer, String>>} and the nesting
	 * level is 2, the nested type descriptor will be String, derived from the map value.
	 * <p>Returns {@code null} if a nested type cannot be obtained because it was not declared.
	 * For example, if the field is a {@code List<?>}, the nested type descriptor returned will be {@code null}.
	 * @param field the field
	 * @param nestingLevel the nesting level of the collection/array element or
	 * map key/value declaration within the field
	 * @return the nested type descriptor at the specified nesting level, or null
	 * if it could not be obtained
	 * @throws IllegalArgumentException if the types up to the specified nesting
	 * level are not of collection, array, or map types
	 */
	public static TypeDescriptor nested(Field field, int nestingLevel) {
		return nested(new TypeDescriptor(field), nestingLevel);
	}

	/**
	 * Creates a type descriptor for a nested type declared within the property.
	 * <p>For example, if the property is a {@code List<String>} and the nesting
	 * level is 1, the nested type descriptor will be {@code String.class}.
	 * <p>If the property is a {@code List<List<String>>} and the nesting level
	 * is 2, the nested type descriptor will also be a {@code String.class}.
	 * <p>If the property is a {@code Map<Integer, String>} and the nesting level
	 * is 1, the nested type descriptor will be String, derived from the map value.
	 * <p>If the property is a {@code List<Map<Integer, String>>} and the nesting
	 * level is 2, the nested type descriptor will be String, derived from the map value.
	 * <p>Returns {@code null} if a nested type cannot be obtained because it was not declared.
	 * For example, if the property is a {@code List<?>}, the nested type descriptor
	 * returned will be {@code null}.
	 * @param property the property
	 * @param nestingLevel the nesting level of the collection/array element or
	 * map key/value declaration within the property
	 * @return the nested type descriptor at the specified nesting level, or
	 * {@code null} if it could not be obtained
	 * @throws IllegalArgumentException if the types up to the specified nesting
	 * level are not of collection, array, or map types
	 */
	public static TypeDescriptor nested(Property property, int nestingLevel) {
		return nested(new TypeDescriptor(property), nestingLevel);
	}

	/**
	 * Create a new type descriptor for an object.
	 * <p>Use this factory method to introspect a source object before asking the conversion system to convert it to some another type.
	 * <p>If the provided object is null, returns null, else calls {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
	 * @param source the source object
	 * @return the type descriptor
	 */
	public static TypeDescriptor forObject(Object source) {
		return (source != null ? valueOf(source.getClass()) : null);
	}

	private static TypeDescriptor nested(TypeDescriptor typeDescriptor, int nestingLevel) {
		ResolvableType nested = typeDescriptor.resolvableType;
		for (int i = 0; i < nestingLevel; i++) {
			if (Object.class.equals(nested.getType())) {
				// could be a collection type but we don't know about its element type,
				// so let's just assume there is an element type of type Object
			} else {
				nested = nested.getNested(2);
			}
		}
		Assert.state(nested != ResolvableType.NONE, "Unable to obtain nested generic from "
					+ typeDescriptor + " at level " + nestingLevel);
		return getRelatedIfResolvable(typeDescriptor, nested);
	}

	private static TypeDescriptor getRelatedIfResolvable(TypeDescriptor source, ResolvableType type) {
		if (type.resolve() == null) {
			return null;
		}
		return new TypeDescriptor(type, null, source.annotations);
656 657
	}

J
Juergen Hoeller 已提交
658
}