diff --git a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java index 22e0ec58ca8..ee896219602 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -231,6 +231,7 @@ public class FormattingConversionService extends GenericConversionService public AnnotationPrinterConverter(Class annotationType, AnnotationFormatterFactory annotationFormatterFactory, Class fieldType) { + this.annotationType = annotationType; this.annotationFormatterFactory = annotationFormatterFactory; this.fieldType = fieldType; @@ -284,6 +285,7 @@ public class FormattingConversionService extends GenericConversionService public AnnotationParserConverter(Class annotationType, AnnotationFormatterFactory annotationFormatterFactory, Class fieldType) { + this.annotationType = annotationType; this.annotationFormatterFactory = annotationFormatterFactory; this.fieldType = fieldType; @@ -350,16 +352,13 @@ public class FormattingConversionService extends GenericConversionService if (this == other) { return true; } - if (!(other instanceof AnnotationConverterKey)) { - return false; - } AnnotationConverterKey otherKey = (AnnotationConverterKey) other; - return (this.annotation.equals(otherKey.annotation) && this.fieldType.equals(otherKey.fieldType)); + return (this.fieldType == otherKey.fieldType && this.annotation.equals(otherKey.annotation)); } @Override public int hashCode() { - return (this.annotation.hashCode() + 29 * this.fieldType.hashCode()); + return (this.fieldType.hashCode() * 29 + this.annotation.hashCode()); } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index c4115d80371..0faabf6ec8b 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -72,7 +72,7 @@ public class TypeDescriptor implements Serializable { 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 */ public TypeDescriptor(MethodParameter methodParameter) { - Assert.notNull(methodParameter, "MethodParameter must not be null"); this.resolvableType = ResolvableType.forMethodParameter(methodParameter); this.type = this.resolvableType.resolve(methodParameter.getParameterType()); this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ? @@ -95,7 +94,6 @@ public class TypeDescriptor implements Serializable { * @param field the field */ public TypeDescriptor(Field field) { - Assert.notNull(field, "Field must not be null"); this.resolvableType = ResolvableType.forField(field); this.type = this.resolvableType.resolve(field.getType()); this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations()); @@ -121,6 +119,7 @@ public class TypeDescriptor implements Serializable { * @param resolvableType the resolvable type * @param type the backing type (or {@code null} if it should get resolved) * @param annotations the type annotations + * @since 4.0 */ protected TypeDescriptor(ResolvableType resolvableType, Class type, Annotation[] annotations) { 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() { 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 */ public Annotation[] getAnnotations() { @@ -240,6 +239,11 @@ public class TypeDescriptor implements Serializable { * @return true if the annotation is present */ public boolean hasAnnotation(Class 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); } @@ -251,6 +255,11 @@ public class TypeDescriptor implements Serializable { */ @SuppressWarnings("unchecked") public T getAnnotation(Class 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); } @@ -434,37 +443,51 @@ public class TypeDescriptor implements Serializable { } @Override - public boolean equals(Object obj) { - if (this == obj) { + public boolean equals(Object other) { + if (this == other) { return true; } - if (!(obj instanceof TypeDescriptor)) { + if (!(other instanceof TypeDescriptor)) { return false; } - TypeDescriptor other = (TypeDescriptor) obj; - if (!ObjectUtils.nullSafeEquals(this.type, other.type)) { + TypeDescriptor otherDesc = (TypeDescriptor) other; + if (getType() != otherDesc.getType()) { return false; } - if (getAnnotations().length != other.getAnnotations().length) { + if (!annotationsMatch(otherDesc)) { return false; } - for (Annotation ann : getAnnotations()) { - if (!ann.equals(other.getAnnotation(ann.annotationType()))) { - return false; - } - } if (isCollection() || isArray()) { - return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), other.getElementTypeDescriptor()); + return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), otherDesc.getElementTypeDescriptor()); } else if (isMap()) { - return ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) && - ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor()); + return (ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), otherDesc.getMapKeyTypeDescriptor()) && + ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor())); } else { 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 public int hashCode() { return getType().hashCode(); @@ -480,6 +503,20 @@ public class TypeDescriptor implements Serializable { return builder.toString(); } + + /** + * Create a new type descriptor for an object. + *

Use this factory method to introspect a source object before asking the + * conversion system to convert it to some another type. + *

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. *

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); } - /** - * Create a new type descriptor for an object. - *

Use this factory method to introspect a source object before asking the - * conversion system to convert it to some another type. - *

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) { ResolvableType nested = typeDescriptor.resolvableType; for (int i = 0; i < nestingLevel; i++) { @@ -723,6 +747,10 @@ public class TypeDescriptor implements Serializable { return getAnnotations(); } + public boolean isEmpty() { + return ObjectUtils.isEmpty(this.annotations); + } + @Override public boolean equals(Object other) { return (this == other || (other instanceof AnnotatedElementAdapter && diff --git a/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java b/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java index 39184c2c778..9fd16139b65 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/TypeDescriptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -533,7 +533,7 @@ public class TypeDescriptorTests { public void elementTypePreserveContext() throws Exception { TypeDescriptor desc = new TypeDescriptor(getClass().getField("listPreserveContext")); assertEquals(Integer.class, desc.getElementTypeDescriptor().getElementTypeDescriptor().getType()); - List value = new ArrayList(3); + List value = new ArrayList<>(3); desc = desc.elementTypeDescriptor(value); assertEquals(Integer.class, desc.getElementTypeDescriptor().getType()); assertNotNull(desc.getAnnotation(FieldAnnotation.class)); @@ -551,7 +551,7 @@ public class TypeDescriptorTests { public void mapKeyTypePreserveContext() throws Exception { TypeDescriptor desc = new TypeDescriptor(getClass().getField("mapPreserveContext")); assertEquals(Integer.class, desc.getMapKeyTypeDescriptor().getElementTypeDescriptor().getType()); - List value = new ArrayList(3); + List value = new ArrayList<>(3); desc = desc.getMapKeyTypeDescriptor(value); assertEquals(Integer.class, desc.getElementTypeDescriptor().getType()); assertNotNull(desc.getAnnotation(FieldAnnotation.class)); @@ -569,7 +569,7 @@ public class TypeDescriptorTests { public void mapValueTypePreserveContext() throws Exception { TypeDescriptor desc = new TypeDescriptor(getClass().getField("mapPreserveContext")); assertEquals(Integer.class, desc.getMapValueTypeDescriptor().getElementTypeDescriptor().getType()); - List value = new ArrayList(3); + List value = new ArrayList<>(3); desc = desc.getMapValueTypeDescriptor(value); assertEquals(Integer.class, desc.getElementTypeDescriptor().getType()); assertNotNull(desc.getAnnotation(FieldAnnotation.class)); @@ -598,15 +598,16 @@ public class TypeDescriptorTests { TypeDescriptor t12 = new TypeDescriptor(getClass().getField("mapField")); assertEquals(t11, t12); - TypeDescriptor t13 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); - TypeDescriptor t14 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); + MethodParameter testAnnotatedMethod = new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0); + TypeDescriptor t13 = new TypeDescriptor(testAnnotatedMethod); + TypeDescriptor t14 = new TypeDescriptor(testAnnotatedMethod); assertEquals(t13, t14); - TypeDescriptor t15 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); + TypeDescriptor t15 = new TypeDescriptor(testAnnotatedMethod); TypeDescriptor t16 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethodDifferentAnnotationValue", String.class), 0)); assertNotEquals(t15, t16); - TypeDescriptor t17 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0)); + TypeDescriptor t17 = new TypeDescriptor(testAnnotatedMethod); TypeDescriptor t18 = new TypeDescriptor(new MethodParameter(getClass().getMethod("test5", String.class), 0)); assertNotEquals(t17, t18); } @@ -841,19 +842,19 @@ public class TypeDescriptorTests { public List listOfString; - public List> listOfListOfString = new ArrayList>(); + public List> listOfListOfString = new ArrayList<>(); - public List listOfListOfUnknown = new ArrayList(); + public List listOfListOfUnknown = new ArrayList<>(); public int[] intArray; public List[] arrayOfListOfString; - public List listField = new ArrayList(); + public List listField = new ArrayList<>(); - public Map mapField = new HashMap(); + public Map mapField = new HashMap<>(); - public Map> nestedMapField = new HashMap>(); + public Map> nestedMapField = new HashMap<>(); public Map, List> fieldMap; @@ -879,9 +880,9 @@ public class TypeDescriptorTests { public Map isAssignableMapKeyValueTypes; - public MultiValueMap multiValueMap = new LinkedMultiValueMap(); + public MultiValueMap multiValueMap = new LinkedMultiValueMap<>(); - public PassDownGeneric passDownGeneric = new PassDownGeneric(); + public PassDownGeneric passDownGeneric = new PassDownGeneric<>(); // Classes designed for test introspection