diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index 4eff2715713..22e468b2b66 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -30,6 +30,7 @@ import org.springframework.core.CollectionFactory; import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.type.filter.TypeFilter; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -131,7 +132,7 @@ class TypeConverterDelegate { // No custom editor but custom ConversionService specified? ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); if (editor == null && conversionService != null && convertedValue != null) { - TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(convertedValue); + TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); TypeDescriptor targetTypeDesc = typeDescriptor; if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) { return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc); @@ -458,7 +459,7 @@ class TypeConverterDelegate { if (!originalAllowed && !Collection.class.isAssignableFrom(requiredType)) { return original; } - + typeDescriptor = typeDescriptor.narrow(original); TypeDescriptor elementType = typeDescriptor.getElementType(); if (elementType == null && originalAllowed && !this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) { @@ -530,7 +531,7 @@ class TypeConverterDelegate { if (!originalAllowed && !Map.class.isAssignableFrom(requiredType)) { return original; } - + typeDescriptor = typeDescriptor.narrow(original); TypeDescriptor keyType = typeDescriptor.getMapKeyType(); TypeDescriptor valueType = typeDescriptor.getMapValueType(); if (keyType == null && valueType == null && originalAllowed && diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java index cbe2adbe1ba..e48bcc5bec6 100644 --- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java +++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java @@ -96,12 +96,21 @@ public class CallbacksSecurityTests { public void setProperty(Object value) { checkCurrentContext(); } - + public Object getProperty() { checkCurrentContext(); return null; } + public void setListProperty(Object value) { + checkCurrentContext(); + } + + public Object getListProperty() { + checkCurrentContext(); + return null; + } + private void checkCurrentContext() { assertEquals(expectedName, getCurrentSubjectName()); } diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/callbacks.xml b/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/callbacks.xml index dcbad5a5213..a57670e789a 100644 --- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/callbacks.xml +++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/support/security/callbacks.xml @@ -82,6 +82,12 @@ + + + foo + bar + + \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 6749804565e..2d7c34b45af 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -186,6 +186,13 @@ public class TypeDescriptor { return nested(new BeanPropertyDescriptor(beanClass, 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 null, returns null, else calls {@link #valueOf(Class)} to build a TypeDescriptor from the object's class. + * @param object the source object + * @return the type descriptor + */ public static TypeDescriptor forObject(Object source) { return source != null ? valueOf(source.getClass()) : null; } @@ -206,12 +213,24 @@ public class TypeDescriptor { return ClassUtils.resolvePrimitiveIfNecessary(getType()); } - public TypeDescriptor narrowType(Object value) { + /** + * Narrows this {@link TypeDescriptor} by setting its type to the class of the provided value. + * If the value is null, no narrowing is performed and this TypeDescriptor is returned unchanged. + * Designed to be called by binding frameworks when they read property, field, or method return values. + * Allows such frameworks to narrow a TypeDescriptor built from a declared property, field, or method return value type. + * For example, a field declared as java.lang.Object would be narrowed to java.util.HashMap if it was set to a java.util.HashMap value. + * The narrowed TypeDescriptor can then be used to convert the HashMap to some other type. + * Annotation and nested type context is preserved by the narrowed copy. + * @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) { if (value == null) { return this; } return new TypeDescriptor(value.getClass(), elementType, mapKeyType, mapValueType, annotations); } + /** * Returns the name of this type: the fully qualified class name. */ @@ -292,15 +311,19 @@ public class TypeDescriptor { * @throws IllegalStateException if this type is not a java.util.Collection or Array type */ public TypeDescriptor getElementType() { - if (!isCollection() && !isArray()) { - throw new IllegalStateException("Not a java.util.Collection or Array"); - } + assertCollectionOrArray(); return this.elementType; } + /** + * Creates a elementType descriptor from the provided collection or array element. + * @param element the collection or array element + * @return the element type descriptor + */ public TypeDescriptor elementType(Object element) { + assertCollectionOrArray(); if (elementType != null) { - return elementType.narrowType(element); + return elementType.narrow(element); } else { return element != null ? new TypeDescriptor(element.getClass(), null, null, null, annotations) : null; } @@ -322,15 +345,19 @@ public class TypeDescriptor { * @throws IllegalStateException if this type is not a java.util.Map. */ public TypeDescriptor getMapKeyType() { - if (!isMap()) { - throw new IllegalStateException("Not a map"); - } + assertMap(); return this.mapKeyType; } + /** + * Creates a mapKeyType descriptor from the provided map key. + * @param mapKey the map key + * @return the map key type descriptor + */ public TypeDescriptor mapKeyType(Object mapKey) { + assertMap(); if (mapKeyType != null) { - return mapKeyType.narrowType(mapKey); + return mapKeyType.narrow(mapKey); } else { return mapKey != null ? new TypeDescriptor(mapKey.getClass(), null, null, null, annotations) : null; } @@ -343,15 +370,19 @@ public class TypeDescriptor { * @throws IllegalStateException if this type is not a java.util.Map. */ public TypeDescriptor getMapValueType() { - if (!isMap()) { - throw new IllegalStateException("Not a map"); - } + assertMap(); return this.mapValueType; } + /** + * Creates a mapValueType descriptor from the provided map value. + * @param mapValue the map value + * @return the map value type descriptor + */ public TypeDescriptor mapValueType(Object mapValue) { + assertMap(); if (mapValueType != null) { - return mapValueType.narrowType(mapValue); + return mapValueType.narrow(mapValue); } else { return mapValue != null ? new TypeDescriptor(mapValue.getClass(), null, null, null, annotations) : null; } @@ -481,6 +512,18 @@ public class TypeDescriptor { return valueType.isAssignableTo(targetValueType); } + private void assertCollectionOrArray() { + if (!isCollection() && !isArray()) { + throw new IllegalStateException("Not a java.util.Collection or Array"); + } + } + + private void assertMap() { + if (!isMap()) { + throw new IllegalStateException("Not a java.util.Map"); + } + } + private String wildcard(TypeDescriptor nestedType) { return nestedType != null ? nestedType.toString() : "?"; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java index 8d5283040b0..edac92f0953 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java @@ -106,7 +106,7 @@ public class FunctionReference extends SpelNodeImpl { try { ReflectionUtils.makeAccessible(method); Object result = method.invoke(method.getClass(), functionArgs); - return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1)).narrowType(result)); + return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1)).narrow(result)); } catch (Exception ex) { throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL, diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java index f83b4c0415f..42868bdc954 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -30,7 +30,6 @@ import org.springframework.expression.spel.SpelMessage; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.MethodInvoker; -import org.springframework.util.ObjectUtils; /** * Utility methods used by the reflection resolver code to discover the appropriate @@ -219,13 +218,12 @@ public class ReflectionHelper { // All remaining parameters must be of this type or convertable to this type for (int i = expectedArgTypes.size() - 1; i < suppliedArgTypes.size(); i++) { TypeDescriptor suppliedArg = suppliedArgTypes.get(i); - if (!ObjectUtils.nullSafeEquals(varargsParameterType, suppliedArg)) { - if (suppliedArg == null) { - if (varargsParameterType.isPrimitive()) { - match = null; - } - } - else { + if (suppliedArg == null) { + if (varargsParameterType.isPrimitive()) { + match = null; + } + } else { + if (varargsParameterType != suppliedArg.getType()) { if (ClassUtils.isAssignable(varargsParameterType, suppliedArg.getType())) { if (match != ArgsMatchKind.REQUIRES_CONVERSION) { match = ArgsMatchKind.CLOSE; diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java index 25d1aad2b76..88996901f11 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java @@ -67,7 +67,7 @@ class ReflectiveMethodExecutor implements MethodExecutor { } ReflectionUtils.makeAccessible(this.method); Object value = this.method.invoke(target, arguments); - return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrowType(value)); + return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrow(value)); } catch (Exception ex) { throw new AccessException("Problem invoking method: " + this.method, ex); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 7718f8694db..e79effd3713 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -509,7 +509,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ReflectionUtils.makeAccessible((Method) member); } Object value = ((Method) member).invoke(target); - return new TypedValue(value, typeDescriptor.narrowType(value)); + return new TypedValue(value, typeDescriptor.narrow(value)); } catch (Exception ex) { throw new AccessException("Unable to access property '" + name + "' through getter", ex); @@ -521,7 +521,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { ReflectionUtils.makeAccessible((Field)member); } Object value = ((Field)member).get(target); - return new TypedValue(value, typeDescriptor.narrowType(value)); + return new TypedValue(value, typeDescriptor.narrow(value)); } catch (Exception ex) { throw new AccessException("Unable to access field: " + name, ex);