Skip to content

Commit

Permalink
Convert single null argument to Optional.empty() in SpEL varargs expr…
Browse files Browse the repository at this point in the history
…ession

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
  • Loading branch information
sbrannen committed Dec 10, 2021
1 parent ad7cdc5 commit b2e94f6
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;

import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
Expand Down Expand Up @@ -290,21 +291,29 @@ static boolean convertArguments(TypeConverter converter, Object[] arguments, Exe
Object argument = arguments[varargsPosition];
TypeDescriptor targetType = new TypeDescriptor(methodParam);
TypeDescriptor sourceType = TypeDescriptor.forObject(argument);
// If the argument is null or 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.
if (argument != null && !sourceType.equals(targetType.getElementTypeDescriptor())) {
if (argument == null) {
// Perform the equivalent of GenericConversionService.convertNullSource() for a single argument.
if (targetType.getElementTypeDescriptor().getObjectType() == Optional.class) {
arguments[varargsPosition] = Optional.empty();
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);
}
// 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.
// 2) the input argument was correct type but not wrapped in an array, and nothing was done.
// 3) the input argument was already compatible (i.e., array of valid type), and nothing was done.
// 4) the input argument was the wrong type and got converted and wrapped in an array.
// 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 correct type but not wrapped in an array, and nothing was done.
// 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] &&
!isFirstEntryInArray(argument, arguments[varargsPosition])) {
conversionOccurred = true; // case 3
conversionOccurred = true; // case 5
}
}
// Otherwise, convert remaining arguments to the varargs element type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,7 @@ public void testVarargsOptionalInvocation() {
evaluate("optionalVarargsMethod(2,3)", "[Optional[2], Optional[3]]", 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);
// The following should actually evaluate to [Optional.empty] instead of [null],
// 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)", "[Optional.empty]", 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);
}
Expand Down

0 comments on commit b2e94f6

Please sign in to comment.