|
|
|
@ -72,7 +72,7 @@ public class TypeDescriptor implements Serializable { |
|
|
|
|
|
|
|
|
|
|
|
private final ResolvableType resolvableType; |
|
|
|
private final ResolvableType resolvableType; |
|
|
|
|
|
|
|
|
|
|
|
private final AnnotatedElement annotatedElement; |
|
|
|
private final AnnotatedElementAdapter annotatedElement; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -82,7 +82,6 @@ public class TypeDescriptor implements Serializable { |
|
|
|
* @param methodParameter the method parameter |
|
|
|
* @param methodParameter the method parameter |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public TypeDescriptor(MethodParameter methodParameter) { |
|
|
|
public TypeDescriptor(MethodParameter methodParameter) { |
|
|
|
Assert.notNull(methodParameter, "MethodParameter must not be null"); |
|
|
|
|
|
|
|
this.resolvableType = ResolvableType.forMethodParameter(methodParameter); |
|
|
|
this.resolvableType = ResolvableType.forMethodParameter(methodParameter); |
|
|
|
this.type = this.resolvableType.resolve(methodParameter.getParameterType()); |
|
|
|
this.type = this.resolvableType.resolve(methodParameter.getParameterType()); |
|
|
|
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ? |
|
|
|
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ? |
|
|
|
@ -95,7 +94,6 @@ public class TypeDescriptor implements Serializable { |
|
|
|
* @param field the field |
|
|
|
* @param field the field |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public TypeDescriptor(Field field) { |
|
|
|
public TypeDescriptor(Field field) { |
|
|
|
Assert.notNull(field, "Field must not be null"); |
|
|
|
|
|
|
|
this.resolvableType = ResolvableType.forField(field); |
|
|
|
this.resolvableType = ResolvableType.forField(field); |
|
|
|
this.type = this.resolvableType.resolve(field.getType()); |
|
|
|
this.type = this.resolvableType.resolve(field.getType()); |
|
|
|
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations()); |
|
|
|
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations()); |
|
|
|
@ -121,6 +119,7 @@ public class TypeDescriptor implements Serializable { |
|
|
|
* @param resolvableType the resolvable type |
|
|
|
* @param resolvableType the resolvable type |
|
|
|
* @param type the backing type (or {@code null} if it should get resolved) |
|
|
|
* @param type the backing type (or {@code null} if it should get resolved) |
|
|
|
* @param annotations the type annotations |
|
|
|
* @param annotations the type annotations |
|
|
|
|
|
|
|
* @since 4.0 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected TypeDescriptor(ResolvableType resolvableType, Class<?> type, Annotation[] annotations) { |
|
|
|
protected TypeDescriptor(ResolvableType resolvableType, Class<?> type, Annotation[] annotations) { |
|
|
|
this.resolvableType = resolvableType; |
|
|
|
this.resolvableType = resolvableType; |
|
|
|
@ -211,7 +210,7 @@ public class TypeDescriptor implements Serializable { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Returns the name of this type: the fully qualified class name. |
|
|
|
* Return the name of this type: the fully qualified class name. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public String getName() { |
|
|
|
public String getName() { |
|
|
|
return ClassUtils.getQualifiedName(getType()); |
|
|
|
return ClassUtils.getQualifiedName(getType()); |
|
|
|
@ -225,7 +224,7 @@ public class TypeDescriptor implements Serializable { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* The annotations associated with this type descriptor, if any. |
|
|
|
* Return the annotations associated with this type descriptor, if any. |
|
|
|
* @return the annotations, or an empty array if none |
|
|
|
* @return the annotations, or an empty array if none |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public Annotation[] getAnnotations() { |
|
|
|
public Annotation[] getAnnotations() { |
|
|
|
@ -240,6 +239,11 @@ public class TypeDescriptor implements Serializable { |
|
|
|
* @return <tt>true</tt> if the annotation is present |
|
|
|
* @return <tt>true</tt> if the annotation is present |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public boolean hasAnnotation(Class<? extends Annotation> annotationType) { |
|
|
|
public boolean hasAnnotation(Class<? extends Annotation> annotationType) { |
|
|
|
|
|
|
|
if (this.annotatedElement.isEmpty()) { |
|
|
|
|
|
|
|
// Shortcut: AnnotatedElementUtils would have to expect AnnotatedElement.getAnnotations()
|
|
|
|
|
|
|
|
// to return a copy of the array, whereas we can do it more efficiently here.
|
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
return AnnotatedElementUtils.isAnnotated(this.annotatedElement, annotationType); |
|
|
|
return AnnotatedElementUtils.isAnnotated(this.annotatedElement, annotationType); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -251,6 +255,11 @@ public class TypeDescriptor implements Serializable { |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
public <T extends Annotation> T getAnnotation(Class<T> annotationType) { |
|
|
|
public <T extends Annotation> T getAnnotation(Class<T> annotationType) { |
|
|
|
|
|
|
|
if (this.annotatedElement.isEmpty()) { |
|
|
|
|
|
|
|
// Shortcut: AnnotatedElementUtils would have to expect AnnotatedElement.getAnnotations()
|
|
|
|
|
|
|
|
// to return a copy of the array, whereas we can do it more efficiently here.
|
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
return AnnotatedElementUtils.getMergedAnnotation(this.annotatedElement, annotationType); |
|
|
|
return AnnotatedElementUtils.getMergedAnnotation(this.annotatedElement, annotationType); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -434,37 +443,51 @@ public class TypeDescriptor implements Serializable { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean equals(Object obj) { |
|
|
|
public boolean equals(Object other) { |
|
|
|
if (this == obj) { |
|
|
|
if (this == other) { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
if (!(obj instanceof TypeDescriptor)) { |
|
|
|
if (!(other instanceof TypeDescriptor)) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
TypeDescriptor other = (TypeDescriptor) obj; |
|
|
|
TypeDescriptor otherDesc = (TypeDescriptor) other; |
|
|
|
if (!ObjectUtils.nullSafeEquals(this.type, other.type)) { |
|
|
|
if (getType() != otherDesc.getType()) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
if (getAnnotations().length != other.getAnnotations().length) { |
|
|
|
if (!annotationsMatch(otherDesc)) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
for (Annotation ann : getAnnotations()) { |
|
|
|
|
|
|
|
if (!ann.equals(other.getAnnotation(ann.annotationType()))) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (isCollection() || isArray()) { |
|
|
|
if (isCollection() || isArray()) { |
|
|
|
return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), other.getElementTypeDescriptor()); |
|
|
|
return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), otherDesc.getElementTypeDescriptor()); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (isMap()) { |
|
|
|
else if (isMap()) { |
|
|
|
return ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) && |
|
|
|
return (ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), otherDesc.getMapKeyTypeDescriptor()) && |
|
|
|
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor()); |
|
|
|
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor())); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean annotationsMatch(TypeDescriptor otherDesc) { |
|
|
|
|
|
|
|
Annotation[] anns = getAnnotations(); |
|
|
|
|
|
|
|
Annotation[] otherAnns = otherDesc.getAnnotations(); |
|
|
|
|
|
|
|
if (anns == otherAnns) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (anns.length != otherAnns.length) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (anns.length > 0) { |
|
|
|
|
|
|
|
for (int i = 0; i < anns.length; i++) { |
|
|
|
|
|
|
|
if (anns[i] != otherAnns[i]) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public int hashCode() { |
|
|
|
public int hashCode() { |
|
|
|
return getType().hashCode(); |
|
|
|
return getType().hashCode(); |
|
|
|
@ -480,6 +503,20 @@ public class TypeDescriptor implements Serializable { |
|
|
|
return builder.toString(); |
|
|
|
return builder.toString(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 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 {@code null}, returns {@code 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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Create a new type descriptor from the given type. |
|
|
|
* Create a new type descriptor from the given type. |
|
|
|
* <p>Use this to instruct the conversion system to convert an object to a |
|
|
|
* <p>Use this to instruct the conversion system to convert an object to a |
|
|
|
@ -640,19 +677,6 @@ public class TypeDescriptor implements Serializable { |
|
|
|
return nested(new TypeDescriptor(property), 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 {@code null}, returns {@code 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) { |
|
|
|
private static TypeDescriptor nested(TypeDescriptor typeDescriptor, int nestingLevel) { |
|
|
|
ResolvableType nested = typeDescriptor.resolvableType; |
|
|
|
ResolvableType nested = typeDescriptor.resolvableType; |
|
|
|
for (int i = 0; i < nestingLevel; i++) { |
|
|
|
for (int i = 0; i < nestingLevel; i++) { |
|
|
|
@ -723,6 +747,10 @@ public class TypeDescriptor implements Serializable { |
|
|
|
return getAnnotations(); |
|
|
|
return getAnnotations(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isEmpty() { |
|
|
|
|
|
|
|
return ObjectUtils.isEmpty(this.annotations); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean equals(Object other) { |
|
|
|
public boolean equals(Object other) { |
|
|
|
return (this == other || (other instanceof AnnotatedElementAdapter && |
|
|
|
return (this == other || (other instanceof AnnotatedElementAdapter && |
|
|
|
|