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);