Convert single null argument to Optional.empty() in SpEL varargs expression
Prior to this commit, if a single null value was passed to a method with a varargs array of type java.util.Optional, that null value was passed unmodified. On the contrary, a null passed with additional values to such a method resulted in the null being converted to Optional.empty(). This commit ensures that a single null value is also converted to Optional.empty() for such SpEL expressions. Closes gh-27795
This commit is contained in:
parent
ad7cdc5ce9
commit
b2e94f611f
|
@ -20,6 +20,7 @@ import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Executable;
|
import java.lang.reflect.Executable;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
@ -290,21 +291,29 @@ public abstract class ReflectionHelper {
|
||||||
Object argument = arguments[varargsPosition];
|
Object argument = arguments[varargsPosition];
|
||||||
TypeDescriptor targetType = new TypeDescriptor(methodParam);
|
TypeDescriptor targetType = new TypeDescriptor(methodParam);
|
||||||
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
|
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
|
||||||
// If the argument is null or the argument type is equal to the varargs element type,
|
if (argument == null) {
|
||||||
// there is no need to convert it or wrap it in an array. For example, using
|
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
|
||||||
// StringToArrayConverter to convert a String containing a comma would result in the
|
if (targetType.getElementTypeDescriptor().getObjectType() == Optional.class) {
|
||||||
// String being split and repackaged in an array when it should be used as-is.
|
arguments[varargsPosition] = Optional.empty();
|
||||||
if (argument != null && !sourceType.equals(targetType.getElementTypeDescriptor())) {
|
conversionOccurred = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the argument type is equal to the varargs element type, there is no need to
|
||||||
|
// convert it or wrap it in an array. For example, using StringToArrayConverter to
|
||||||
|
// convert a String containing a comma would result in the String being split and
|
||||||
|
// repackaged in an array when it should be used as-is.
|
||||||
|
else if (!sourceType.equals(targetType.getElementTypeDescriptor())) {
|
||||||
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
|
arguments[varargsPosition] = converter.convertValue(argument, sourceType, targetType);
|
||||||
}
|
}
|
||||||
// Possible outcomes of the above if-block:
|
// Possible outcomes of the above if-else block:
|
||||||
// 1) the input argument was null, and nothing was done.
|
// 1) the input argument was null, and nothing was done.
|
||||||
// 2) the input argument was correct type but not wrapped in an array, and nothing was done.
|
// 2) the input argument was null; the varargs element type is Optional; and the argument was converted to Optional.empty().
|
||||||
// 3) the input argument was already compatible (i.e., array of valid type), and nothing was done.
|
// 3) the input argument was correct type but not wrapped in an array, and nothing was done.
|
||||||
// 4) the input argument was the wrong type and got converted and wrapped in an array.
|
// 4) the input argument was already compatible (i.e., array of valid type), and nothing was done.
|
||||||
|
// 5) the input argument was the wrong type and got converted and wrapped in an array.
|
||||||
if (argument != arguments[varargsPosition] &&
|
if (argument != arguments[varargsPosition] &&
|
||||||
!isFirstEntryInArray(argument, arguments[varargsPosition])) {
|
!isFirstEntryInArray(argument, arguments[varargsPosition])) {
|
||||||
conversionOccurred = true; // case 3
|
conversionOccurred = true; // case 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, convert remaining arguments to the varargs element type.
|
// Otherwise, convert remaining arguments to the varargs element type.
|
||||||
|
|
|
@ -302,10 +302,7 @@ public class MethodInvocationTests extends AbstractExpressionTests {
|
||||||
evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class);
|
evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", String.class);
|
||||||
evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class);
|
evaluate("optionalVarargsMethod('a',3.0d)", "[Optional[a], Optional[3.0]]", String.class);
|
||||||
evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class);
|
evaluate("optionalVarargsMethod(new String[]{'a','b','c'})", "[Optional[a], Optional[b], Optional[c]]", String.class);
|
||||||
// The following should actually evaluate to [Optional.empty] instead of [null],
|
evaluate("optionalVarargsMethod(null)", "[Optional.empty]", String.class);
|
||||||
// but ReflectionHelper.convertArguments() currently does not provide explicit
|
|
||||||
// Optional support for a single argument passed to a varargs array.
|
|
||||||
evaluate("optionalVarargsMethod(null)", "[null]", String.class);
|
|
||||||
evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class);
|
evaluate("optionalVarargsMethod(null,'a')", "[Optional.empty, Optional[a]]", String.class);
|
||||||
evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class);
|
evaluate("optionalVarargsMethod('a',null,'b')", "[Optional[a], Optional.empty, Optional[b]]", String.class);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue