@ -16,12 +16,21 @@
@@ -16,12 +16,21 @@
package org.springframework.expression.spel ;
import java.util.Set ;
import org.junit.jupiter.api.BeforeEach ;
import org.junit.jupiter.api.Nested ;
import org.junit.jupiter.api.Test ;
import org.springframework.expression.spel.standard.SpelExpressionParser ;
import org.springframework.core.convert.TypeDescriptor ;
import org.springframework.core.convert.converter.GenericConverter ;
import org.springframework.core.convert.support.DefaultConversionService ;
import org.springframework.expression.Expression ;
import org.springframework.expression.spel.standard.SpelExpression ;
import org.springframework.expression.spel.support.StandardEvaluationContext ;
import org.springframework.expression.spel.support.StandardTypeConverter ;
import org.springframework.expression.spel.support.StandardTypeLocator ;
import org.springframework.lang.Nullable ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType ;
@ -279,4 +288,75 @@ class VariableAndFunctionTests extends AbstractExpressionTests {
@@ -279,4 +288,75 @@ class VariableAndFunctionTests extends AbstractExpressionTests {
public void nonStatic ( ) {
}
@Nested // gh-34371
class VarargsAndPojoToArrayConversionTests {
private final StandardEvaluationContext context = TestScenarioCreator . getTestEvaluationContext ( ) ;
private final ArrayHolder arrayHolder = new ArrayHolder ( "a" , "b" , "c" ) ;
@BeforeEach
void setUp ( ) {
DefaultConversionService conversionService = new DefaultConversionService ( ) ;
conversionService . addConverter ( new ArrayHolderConverter ( ) ) ;
context . setTypeConverter ( new StandardTypeConverter ( conversionService ) ) ;
context . setVariable ( "arrayHolder" , arrayHolder ) ;
}
@Test
void functionWithVarargsAndPojoToArrayConversion ( ) {
// #varargsFunction: static String varargsFunction(String... strings) -> Arrays.toString(strings)
evaluate ( "#varargsFunction(#arrayHolder)" , "[a, b, c]" ) ;
// #varargsObjectFunction: static String varargsObjectFunction(Object... args) -> Arrays.toString(args)
//
// Since ArrayHolder is an "instanceof Object" and Object is the varargs component type,
// we expect the ArrayHolder not to be converted to an array but rather to be passed
// "as is" as a single argument to the varargs method.
evaluate ( "#varargsObjectFunction(#arrayHolder)" , "[" + arrayHolder . toString ( ) + "]" ) ;
}
@Test
void functionWithVarargsAndPojoToArrayConversionViaMethodHandle ( ) {
// #varargsFunctionHandle: static String varargsFunction(String... strings) -> Arrays.toString(strings)
evaluate ( "#varargsFunctionHandle(#arrayHolder)" , "[a, b, c]" ) ;
// #varargsObjectFunctionHandle: static String varargsObjectFunction(Object... args) -> Arrays.toString(args)
//
// Since ArrayHolder is an "instanceof Object" and Object is the varargs component type,
// we expect the ArrayHolder not to be converted to an array but rather to be passed
// "as is" as a single argument to the varargs method.
evaluate ( "#varargsObjectFunctionHandle(#arrayHolder)" , "[" + arrayHolder . toString ( ) + "]" ) ;
}
private void evaluate ( String expression , Object expectedValue ) {
Expression expr = parser . parseExpression ( expression ) ;
assertThat ( expr ) . as ( "expression" ) . isNotNull ( ) ;
Object value = expr . getValue ( context ) ;
assertThat ( value ) . as ( "expression '" + expression + "'" ) . isEqualTo ( expectedValue ) ;
}
record ArrayHolder ( String . . . array ) {
}
static class ArrayHolderConverter implements GenericConverter {
@Nullable
@Override
public Set < ConvertiblePair > getConvertibleTypes ( ) {
return Set . of ( new ConvertiblePair ( ArrayHolder . class , Object [ ] . class ) ) ;
}
@Nullable
@Override
public String [ ] convert ( @Nullable Object source , TypeDescriptor sourceType , TypeDescriptor targetType ) {
return ( ( ArrayHolder ) source ) . array ( ) ;
}
}
}
}