Polish SpEL Javadocs and internals
This commit is contained in:
parent
1080c145e3
commit
888e50175d
|
@ -80,7 +80,7 @@ public class CompositeStringExpression implements Expression {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
|
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
|
||||||
Object value = getValue();
|
String value = getValue();
|
||||||
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
|
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ public class CompositeStringExpression implements Expression {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T> T getValue(@Nullable Object rootObject, @Nullable Class<T> desiredResultType) throws EvaluationException {
|
public <T> T getValue(@Nullable Object rootObject, @Nullable Class<T> desiredResultType) throws EvaluationException {
|
||||||
Object value = getValue(rootObject);
|
String value = getValue(rootObject);
|
||||||
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
|
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ public class CompositeStringExpression implements Expression {
|
||||||
public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResultType)
|
public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResultType)
|
||||||
throws EvaluationException {
|
throws EvaluationException {
|
||||||
|
|
||||||
Object value = getValue(context);
|
String value = getValue(context);
|
||||||
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
|
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ public class CompositeStringExpression implements Expression {
|
||||||
public <T> T getValue(EvaluationContext context, @Nullable Object rootObject, @Nullable Class<T> desiredResultType)
|
public <T> T getValue(EvaluationContext context, @Nullable Object rootObject, @Nullable Class<T> desiredResultType)
|
||||||
throws EvaluationException {
|
throws EvaluationException {
|
||||||
|
|
||||||
Object value = getValue(context,rootObject);
|
String value = getValue(context,rootObject);
|
||||||
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), desiredResultType);
|
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), desiredResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class LiteralExpression implements Expression {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
|
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
|
||||||
Object value = getValue();
|
String value = getValue();
|
||||||
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
|
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ public class LiteralExpression implements Expression {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T> T getValue(@Nullable Object rootObject, @Nullable Class<T> desiredResultType) throws EvaluationException {
|
public <T> T getValue(@Nullable Object rootObject, @Nullable Class<T> desiredResultType) throws EvaluationException {
|
||||||
Object value = getValue(rootObject);
|
String value = getValue(rootObject);
|
||||||
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
|
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +87,8 @@ public class LiteralExpression implements Expression {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResultType)
|
public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResultType) throws EvaluationException {
|
||||||
throws EvaluationException {
|
String value = getValue(context);
|
||||||
|
|
||||||
Object value = getValue(context);
|
|
||||||
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
|
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +102,7 @@ public class LiteralExpression implements Expression {
|
||||||
public <T> T getValue(EvaluationContext context, @Nullable Object rootObject, @Nullable Class<T> desiredResultType)
|
public <T> T getValue(EvaluationContext context, @Nullable Object rootObject, @Nullable Class<T> desiredResultType)
|
||||||
throws EvaluationException {
|
throws EvaluationException {
|
||||||
|
|
||||||
Object value = getValue(context, rootObject);
|
String value = getValue(context, rootObject);
|
||||||
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), desiredResultType);
|
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), desiredResultType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -21,7 +21,6 @@ import java.util.StringJoiner;
|
||||||
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ClassUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods (formatters, etc) used during parsing and evaluation.
|
* Utility methods (formatters, etc) used during parsing and evaluation.
|
||||||
|
@ -51,10 +50,9 @@ abstract class FormatHelper {
|
||||||
* <p>A String array will have the formatted name "java.lang.String[]".
|
* <p>A String array will have the formatted name "java.lang.String[]".
|
||||||
* @param clazz the Class whose name is to be formatted
|
* @param clazz the Class whose name is to be formatted
|
||||||
* @return a formatted String suitable for message inclusion
|
* @return a formatted String suitable for message inclusion
|
||||||
* @see ClassUtils#getQualifiedName(Class)
|
|
||||||
*/
|
*/
|
||||||
static String formatClassNameForMessage(@Nullable Class<?> clazz) {
|
static String formatClassNameForMessage(@Nullable Class<?> clazz) {
|
||||||
return (clazz != null ? ClassUtils.getQualifiedName(clazz) : "null");
|
return (clazz != null ? clazz.getTypeName() : "null");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -39,16 +39,21 @@ import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined
|
* A function reference is of the form "#someFunction(a,b,c)".
|
||||||
* in the context prior to the expression being evaluated. Functions may also be static
|
|
||||||
* Java methods, registered in the context prior to invocation of the expression.
|
|
||||||
*
|
*
|
||||||
* <p>Functions are very simplistic. The arguments are not part of the definition
|
* <p>Functions can be either a {@link Method} (for static Java methods) or a
|
||||||
* (right now), so the names must be unique.
|
* {@link MethodHandle} and must be registered in the context prior to evaluation
|
||||||
|
* of the expression. See the {@code registerFunction()} methods in
|
||||||
|
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}
|
||||||
|
* for details.
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
|
* @author Simon Baslé
|
||||||
|
* @author Sam Brannen
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
|
* @see org.springframework.expression.spel.support.StandardEvaluationContext#registerFunction(String, Method)
|
||||||
|
* @see org.springframework.expression.spel.support.StandardEvaluationContext#registerFunction(String, MethodHandle)
|
||||||
*/
|
*/
|
||||||
public class FunctionReference extends SpelNodeImpl {
|
public class FunctionReference extends SpelNodeImpl {
|
||||||
|
|
||||||
|
@ -72,39 +77,44 @@ public class FunctionReference extends SpelNodeImpl {
|
||||||
if (value == TypedValue.NULL) {
|
if (value == TypedValue.NULL) {
|
||||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
|
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
|
||||||
}
|
}
|
||||||
Object resolvedValue = value.getValue();
|
Object function = value.getValue();
|
||||||
if (resolvedValue instanceof MethodHandle methodHandle) {
|
|
||||||
|
// Static Java method registered via a Method.
|
||||||
|
// Note: "javaMethod" cannot be named "method" due to a bug in Checkstyle.
|
||||||
|
if (function instanceof Method javaMethod) {
|
||||||
try {
|
try {
|
||||||
return executeFunctionBoundMethodHandle(state, methodHandle);
|
return executeFunctionViaMethod(state, javaMethod);
|
||||||
}
|
}
|
||||||
catch (SpelEvaluationException ex) {
|
catch (SpelEvaluationException ex) {
|
||||||
ex.setPosition(getStartPosition());
|
ex.setPosition(getStartPosition());
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(resolvedValue instanceof Method function)) {
|
|
||||||
// Possibly a static Java method registered as a function
|
// Function registered via a MethodHandle.
|
||||||
|
if (function instanceof MethodHandle methodHandle) {
|
||||||
|
try {
|
||||||
|
return executeFunctionViaMethodHandle(state, methodHandle);
|
||||||
|
}
|
||||||
|
catch (SpelEvaluationException ex) {
|
||||||
|
ex.setPosition(getStartPosition());
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neither a Method nor a MethodHandle?
|
||||||
throw new SpelEvaluationException(
|
throw new SpelEvaluationException(
|
||||||
SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, value.getClass());
|
SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, value.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
return executeFunctionJLRMethod(state, function);
|
|
||||||
}
|
|
||||||
catch (SpelEvaluationException ex) {
|
|
||||||
ex.setPosition(getStartPosition());
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a function represented as a {@code java.lang.reflect.Method}.
|
* Execute a function represented as a {@link Method}.
|
||||||
* @param state the expression evaluation state
|
* @param state the expression evaluation state
|
||||||
* @param method the method to invoke
|
* @param method the method to invoke
|
||||||
* @return the return value of the invoked Java method
|
* @return the return value of the invoked Java method
|
||||||
* @throws EvaluationException if there is any problem invoking the method
|
* @throws EvaluationException if there is any problem invoking the method
|
||||||
*/
|
*/
|
||||||
private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException {
|
private TypedValue executeFunctionViaMethod(ExpressionState state, Method method) throws EvaluationException {
|
||||||
Object[] functionArgs = getArguments(state);
|
Object[] functionArgs = getArguments(state);
|
||||||
|
|
||||||
if (!method.isVarArgs()) {
|
if (!method.isVarArgs()) {
|
||||||
|
@ -151,17 +161,17 @@ public class FunctionReference extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a function represented as {@code java.lang.invoke.MethodHandle}.
|
* Execute a function represented as {@link MethodHandle}.
|
||||||
* Method types that take no arguments (fully bound handles or static methods
|
* <p>Method types that take no arguments (fully bound handles or static methods
|
||||||
* with no parameters) can use {@code #invoke()} which is the most efficient.
|
* with no parameters) can use {@link MethodHandle#invoke()} which is the most
|
||||||
* Otherwise, {@code #invokeWithArguments)} is used.
|
* efficient. Otherwise, {@link MethodHandle#invokeWithArguments()} is used.
|
||||||
* @param state the expression evaluation state
|
* @param state the expression evaluation state
|
||||||
* @param methodHandle the method to invoke
|
* @param methodHandle the method handle to invoke
|
||||||
* @return the return value of the invoked Java method
|
* @return the return value of the invoked Java method
|
||||||
* @throws EvaluationException if there is any problem invoking the method
|
* @throws EvaluationException if there is any problem invoking the method
|
||||||
* @since 6.1
|
* @since 6.1
|
||||||
*/
|
*/
|
||||||
private TypedValue executeFunctionBoundMethodHandle(ExpressionState state, MethodHandle methodHandle) throws EvaluationException {
|
private TypedValue executeFunctionViaMethodHandle(ExpressionState state, MethodHandle methodHandle) throws EvaluationException {
|
||||||
Object[] functionArgs = getArguments(state);
|
Object[] functionArgs = getArguments(state);
|
||||||
MethodType declaredParams = methodHandle.type();
|
MethodType declaredParams = methodHandle.type();
|
||||||
int spelParamCount = functionArgs.length;
|
int spelParamCount = functionArgs.length;
|
||||||
|
@ -169,17 +179,15 @@ public class FunctionReference extends SpelNodeImpl {
|
||||||
|
|
||||||
boolean isSuspectedVarargs = declaredParams.lastParameterType().isArray();
|
boolean isSuspectedVarargs = declaredParams.lastParameterType().isArray();
|
||||||
|
|
||||||
if (spelParamCount < declaredParamCount || (spelParamCount > declaredParamCount
|
if (spelParamCount < declaredParamCount || (spelParamCount > declaredParamCount && !isSuspectedVarargs)) {
|
||||||
&& !isSuspectedVarargs)) {
|
|
||||||
// incorrect number, including more arguments and not a vararg
|
// incorrect number, including more arguments and not a vararg
|
||||||
|
// perhaps a subset of arguments was provided but the MethodHandle wasn't bound?
|
||||||
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
|
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
|
||||||
functionArgs.length, declaredParamCount);
|
functionArgs.length, declaredParamCount);
|
||||||
//perhaps a subset of arguments was provided but the MethodHandle wasn't bound?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// simplest case: the MethodHandle is fully bound or represents a static method with no params:
|
// simplest case: the MethodHandle is fully bound or represents a static method with no params:
|
||||||
if (declaredParamCount == 0) {
|
if (declaredParamCount == 0) {
|
||||||
//note we consider MethodHandles not compilable
|
|
||||||
try {
|
try {
|
||||||
return new TypedValue(methodHandle.invoke());
|
return new TypedValue(methodHandle.invoke());
|
||||||
}
|
}
|
||||||
|
@ -188,6 +196,7 @@ public class FunctionReference extends SpelNodeImpl {
|
||||||
this.name, ex.getMessage());
|
this.name, ex.getMessage());
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
// Note: we consider MethodHandles not compilable
|
||||||
this.exitTypeDescriptor = null;
|
this.exitTypeDescriptor = null;
|
||||||
this.method = null;
|
this.method = null;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +216,6 @@ public class FunctionReference extends SpelNodeImpl {
|
||||||
methodHandle.type().parameterArray(), functionArgs);
|
methodHandle.type().parameterArray(), functionArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
//note we consider MethodHandles not compilable
|
|
||||||
try {
|
try {
|
||||||
return new TypedValue(methodHandle.invokeWithArguments(functionArgs));
|
return new TypedValue(methodHandle.invokeWithArguments(functionArgs));
|
||||||
}
|
}
|
||||||
|
@ -216,6 +224,7 @@ public class FunctionReference extends SpelNodeImpl {
|
||||||
this.name, ex.getMessage());
|
this.name, ex.getMessage());
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
// Note: we consider MethodHandles not compilable
|
||||||
this.exitTypeDescriptor = null;
|
this.exitTypeDescriptor = null;
|
||||||
this.method = null;
|
this.method = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,12 +180,12 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.indexedType = IndexedType.STRING;
|
this.indexedType = IndexedType.STRING;
|
||||||
return new StringIndexingLValue((String) target, idx, targetDescriptor);
|
return new StringIndexingValueRef((String) target, idx, targetDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and treat the index value as a property of the context object
|
// Try and treat the index value as a property of the context object
|
||||||
// TODO: could call the conversion service to convert the value to a String
|
// TODO Could call the conversion service to convert the value to a String
|
||||||
TypeDescriptor valueType = indexValue.getTypeDescriptor();
|
TypeDescriptor valueType = indexValue.getTypeDescriptor();
|
||||||
if (valueType != null && String.class == valueType.getType()) {
|
if (valueType != null && String.class == valueType.getType()) {
|
||||||
this.indexedType = IndexedType.OBJECT;
|
this.indexedType = IndexedType.OBJECT;
|
||||||
|
@ -226,41 +226,42 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.indexedType == IndexedType.ARRAY) {
|
if (this.indexedType == IndexedType.ARRAY) {
|
||||||
int insn;
|
int insn = switch (this.exitTypeDescriptor) {
|
||||||
if ("D".equals(this.exitTypeDescriptor)) {
|
case "D" -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "[D");
|
mv.visitTypeInsn(CHECKCAST, "[D");
|
||||||
insn = DALOAD;
|
yield DALOAD;
|
||||||
}
|
}
|
||||||
else if ("F".equals(this.exitTypeDescriptor)) {
|
case "F" -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "[F");
|
mv.visitTypeInsn(CHECKCAST, "[F");
|
||||||
insn = FALOAD;
|
yield FALOAD;
|
||||||
}
|
}
|
||||||
else if ("J".equals(this.exitTypeDescriptor)) {
|
case "J" -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "[J");
|
mv.visitTypeInsn(CHECKCAST, "[J");
|
||||||
insn = LALOAD;
|
yield LALOAD;
|
||||||
}
|
}
|
||||||
else if ("I".equals(this.exitTypeDescriptor)) {
|
case "I" -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "[I");
|
mv.visitTypeInsn(CHECKCAST, "[I");
|
||||||
insn = IALOAD;
|
yield IALOAD;
|
||||||
}
|
}
|
||||||
else if ("S".equals(this.exitTypeDescriptor)) {
|
case "S" -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "[S");
|
mv.visitTypeInsn(CHECKCAST, "[S");
|
||||||
insn = SALOAD;
|
yield SALOAD;
|
||||||
}
|
}
|
||||||
else if ("B".equals(this.exitTypeDescriptor)) {
|
case "B" -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "[B");
|
mv.visitTypeInsn(CHECKCAST, "[B");
|
||||||
insn = BALOAD;
|
yield BALOAD;
|
||||||
}
|
}
|
||||||
else if ("C".equals(this.exitTypeDescriptor)) {
|
case "C" -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "[C");
|
mv.visitTypeInsn(CHECKCAST, "[C");
|
||||||
insn = CALOAD;
|
yield CALOAD;
|
||||||
}
|
}
|
||||||
else {
|
default -> {
|
||||||
mv.visitTypeInsn(CHECKCAST, "["+ this.exitTypeDescriptor +
|
mv.visitTypeInsn(CHECKCAST, "["+ this.exitTypeDescriptor +
|
||||||
(CodeFlow.isPrimitiveArray(this.exitTypeDescriptor) ? "" : ";"));
|
(CodeFlow.isPrimitiveArray(this.exitTypeDescriptor) ? "" : ";"));
|
||||||
//depthPlusOne(exitTypeDescriptor)+"Ljava/lang/Object;");
|
yield AALOAD;
|
||||||
insn = AALOAD;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SpelNodeImpl index = this.children[0];
|
SpelNodeImpl index = this.children[0];
|
||||||
cf.enterCompilationScope();
|
cf.enterCompilationScope();
|
||||||
index.generateCode(mv, cf);
|
index.generateCode(mv, cf);
|
||||||
|
@ -325,6 +326,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toStringAST() {
|
public String toStringAST() {
|
||||||
|
// TODO Since we do not support multidimensional arrays, we should be able to return: "[" + getChild(0).toStringAST() + "]"
|
||||||
StringJoiner sj = new StringJoiner(",", "[", "]");
|
StringJoiner sj = new StringJoiner(",", "[", "]");
|
||||||
for (int i = 0; i < getChildCount(); i++) {
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
sj.add(getChild(i).toStringAST());
|
sj.add(getChild(i).toStringAST());
|
||||||
|
@ -744,7 +746,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class StringIndexingLValue implements ValueRef {
|
private class StringIndexingValueRef implements ValueRef {
|
||||||
|
|
||||||
private final String target;
|
private final String target;
|
||||||
|
|
||||||
|
@ -752,7 +754,7 @@ public class Indexer extends SpelNodeImpl {
|
||||||
|
|
||||||
private final TypeDescriptor typeDescriptor;
|
private final TypeDescriptor typeDescriptor;
|
||||||
|
|
||||||
public StringIndexingLValue(String target, int index, TypeDescriptor typeDescriptor) {
|
public StringIndexingValueRef(String target, int index, TypeDescriptor typeDescriptor) {
|
||||||
this.target = target;
|
this.target = target;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.typeDescriptor = typeDescriptor;
|
this.typeDescriptor = typeDescriptor;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -111,8 +111,7 @@ public class InlineList extends SpelNodeImpl {
|
||||||
public String toStringAST() {
|
public String toStringAST() {
|
||||||
StringJoiner sj = new StringJoiner(",", "{", "}");
|
StringJoiner sj = new StringJoiner(",", "{", "}");
|
||||||
// String ast matches input string, not the 'toString()' of the resultant collection, which would use []
|
// String ast matches input string, not the 'toString()' of the resultant collection, which would use []
|
||||||
int count = getChildCount();
|
for (int c = 0; c < getChildCount(); c++) {
|
||||||
for (int c = 0; c < count; c++) {
|
|
||||||
sj.add(getChild(c).toStringAST());
|
sj.add(getChild(c).toStringAST());
|
||||||
}
|
}
|
||||||
return sj.toString();
|
return sj.toString();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -143,8 +143,7 @@ public class InlineMap extends SpelNodeImpl {
|
||||||
@Override
|
@Override
|
||||||
public String toStringAST() {
|
public String toStringAST() {
|
||||||
StringBuilder sb = new StringBuilder("{");
|
StringBuilder sb = new StringBuilder("{");
|
||||||
int count = getChildCount();
|
for (int c = 0; c < getChildCount(); c++) {
|
||||||
for (int c = 0; c < count; c++) {
|
|
||||||
if (c > 0) {
|
if (c > 0) {
|
||||||
sb.append(',');
|
sb.append(',');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -60,10 +60,7 @@ public class Projection extends SpelNodeImpl {
|
||||||
@Override
|
@Override
|
||||||
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
|
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
|
||||||
TypedValue op = state.getActiveContextObject();
|
TypedValue op = state.getActiveContextObject();
|
||||||
|
|
||||||
Object operand = op.getValue();
|
Object operand = op.getValue();
|
||||||
boolean operandIsArray = ObjectUtils.isArray(operand);
|
|
||||||
// TypeDescriptor operandTypeDescriptor = op.getTypeDescriptor();
|
|
||||||
|
|
||||||
// When the input is a map, we push a special context object on the stack
|
// When the input is a map, we push a special context object on the stack
|
||||||
// before calling the specified operation. This special context object
|
// before calling the specified operation. This special context object
|
||||||
|
@ -86,6 +83,7 @@ public class Projection extends SpelNodeImpl {
|
||||||
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this); // TODO unable to build correct type descriptor
|
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this); // TODO unable to build correct type descriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean operandIsArray = ObjectUtils.isArray(operand);
|
||||||
if (operand instanceof Iterable || operandIsArray) {
|
if (operand instanceof Iterable || operandIsArray) {
|
||||||
Iterable<?> data = (operand instanceof Iterable<?> iterable ?
|
Iterable<?> data = (operand instanceof Iterable<?> iterable ?
|
||||||
iterable : Arrays.asList(ObjectUtils.toObjectArray(operand)));
|
iterable : Arrays.asList(ObjectUtils.toObjectArray(operand)));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -90,7 +90,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueRef getValueRef(ExpressionState state) throws EvaluationException {
|
public ValueRef getValueRef(ExpressionState state) throws EvaluationException {
|
||||||
return new AccessorLValue(this, state.getActiveContextObject(), state.getEvaluationContext(),
|
return new AccessorValueRef(this, state.getActiveContextObject(), state.getEvaluationContext(),
|
||||||
state.getConfiguration().isAutoGrowNullReferences());
|
state.getConfiguration().isAutoGrowNullReferences());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,7 +391,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class AccessorLValue implements ValueRef {
|
private static class AccessorValueRef implements ValueRef {
|
||||||
|
|
||||||
private final PropertyOrFieldReference ref;
|
private final PropertyOrFieldReference ref;
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||||
|
|
||||||
private final boolean autoGrowNullReferences;
|
private final boolean autoGrowNullReferences;
|
||||||
|
|
||||||
public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject,
|
public AccessorValueRef(PropertyOrFieldReference propertyOrFieldReference, TypedValue activeContextObject,
|
||||||
EvaluationContext evalContext, boolean autoGrowNullReferences) {
|
EvaluationContext evalContext, boolean autoGrowNullReferences) {
|
||||||
|
|
||||||
this.ref = propertyOrFieldReference;
|
this.ref = propertyOrFieldReference;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -19,39 +19,47 @@ package org.springframework.expression.spel.standard;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holder for a kind of token, the associated data and its position in the input data
|
* Holder for a kind of token, the associated data, and its position in the input
|
||||||
* stream (start/end).
|
* data stream (start/end).
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
class Token {
|
class Token {
|
||||||
|
|
||||||
TokenKind kind;
|
final TokenKind kind;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
String data;
|
final String data;
|
||||||
|
|
||||||
int startPos; // index of first character
|
final int startPos;
|
||||||
|
|
||||||
int endPos; // index of char after the last character
|
final int endPos;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for use when there is no particular data for the token
|
* Constructor for use when there is no particular data for the token
|
||||||
* (e.g. TRUE or '+')
|
* (e.g. TRUE or '+').
|
||||||
* @param startPos the exact start
|
* @param tokenKind the kind of token
|
||||||
* @param endPos the index to the last character
|
* @param startPos the exact start position
|
||||||
|
* @param endPos the index of the last character
|
||||||
*/
|
*/
|
||||||
Token(TokenKind tokenKind, int startPos, int endPos) {
|
Token(TokenKind tokenKind, int startPos, int endPos) {
|
||||||
this.kind = tokenKind;
|
this(tokenKind, null, startPos, endPos);
|
||||||
this.startPos = startPos;
|
|
||||||
this.endPos = endPos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for use when there is data for the token.
|
||||||
|
* @param tokenKind the kind of token
|
||||||
|
* @param tokenData the data for the token
|
||||||
|
* @param startPos the exact start position
|
||||||
|
* @param endPos the index of the last character
|
||||||
|
*/
|
||||||
Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) {
|
Token(TokenKind tokenKind, char[] tokenData, int startPos, int endPos) {
|
||||||
this(tokenKind, startPos, endPos);
|
this.kind = tokenKind;
|
||||||
this.data = new String(tokenData);
|
this.data = (tokenData != null ? new String(tokenData) : null);
|
||||||
|
this.startPos = startPos;
|
||||||
|
this.endPos = endPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2023 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -279,7 +279,7 @@ public class StandardEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the specified Method as a SpEL function.
|
* Register the specified {@link Method} as a SpEL function.
|
||||||
* <p>Note: Function names share a namespace with the variables in this
|
* <p>Note: Function names share a namespace with the variables in this
|
||||||
* evaluation context, as populated by {@link #setVariable(String, Object)}.
|
* evaluation context, as populated by {@link #setVariable(String, Object)}.
|
||||||
* Make sure that specified function names and variable names do not overlap.
|
* Make sure that specified function names and variable names do not overlap.
|
||||||
|
@ -292,7 +292,7 @@ public class StandardEvaluationContext implements EvaluationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the specified MethodHandle as a SpEL function.
|
* Register the specified {@link MethodHandle} as a SpEL function.
|
||||||
* <p>Note: Function names share a namespace with the variables in this
|
* <p>Note: Function names share a namespace with the variables in this
|
||||||
* evaluation context, as populated by {@link #setVariable(String, Object)}.
|
* evaluation context, as populated by {@link #setVariable(String, Object)}.
|
||||||
* Make sure that specified function names and variable names do not overlap.
|
* Make sure that specified function names and variable names do not overlap.
|
||||||
|
|
|
@ -112,6 +112,24 @@ class ParsingTests {
|
||||||
parseCheck("#var1='value1'");
|
parseCheck("#var1='value1'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void indexing() {
|
||||||
|
parseCheck("#var[2]");
|
||||||
|
parseCheck("person['name']");
|
||||||
|
parseCheck("person[name]");
|
||||||
|
parseCheck("array[2]");
|
||||||
|
parseCheck("array[2][3]");
|
||||||
|
parseCheck("func()[2]");
|
||||||
|
parseCheck("#func()[2]");
|
||||||
|
parseCheck("'abc'[2]");
|
||||||
|
parseCheck("\"abc\"[2]", "'abc'[2]");
|
||||||
|
parseCheck("{1,2,3}[2]");
|
||||||
|
parseCheck("{'k':'v'}['k']");
|
||||||
|
parseCheck("{'k':'v'}[k]");
|
||||||
|
parseCheck("{'k1':'v1','k2':'v2'}['k2']");
|
||||||
|
parseCheck("{'k1':'v1','k2':'v2'}[k2]");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void projection() {
|
void projection() {
|
||||||
parseCheck("{1,2,3,4,5,6,7,8,9,10}.![#isEven()]");
|
parseCheck("{1,2,3,4,5,6,7,8,9,10}.![#isEven()]");
|
||||||
|
|
|
@ -40,15 +40,22 @@ import org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMat
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.CLOSE;
|
||||||
|
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.EXACT;
|
||||||
|
import static org.springframework.expression.spel.support.ReflectionHelper.ArgumentsMatchKind.REQUIRES_CONVERSION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for reflection helper code.
|
* Tests for {@link ReflectionHelper}, {@link SpelUtilities}, {@link TypedValue},
|
||||||
|
* {@link ReflectivePropertyAccessor}, ...
|
||||||
*
|
*
|
||||||
* @author Andy Clement
|
* @author Andy Clement
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
*/
|
*/
|
||||||
class ReflectionHelperTests extends AbstractExpressionTests {
|
class ReflectionHelperTests extends AbstractExpressionTests {
|
||||||
|
|
||||||
|
private final StandardTypeConverter tc = new StandardTypeConverter();
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void utilities() throws ParseException {
|
void utilities() throws ParseException {
|
||||||
SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2");
|
SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2");
|
||||||
|
@ -100,44 +107,38 @@ class ReflectionHelperTests extends AbstractExpressionTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void reflectionHelperCompareArguments_ExactMatching() {
|
void reflectionHelperCompareArguments_ExactMatching() {
|
||||||
StandardTypeConverter tc = new StandardTypeConverter();
|
|
||||||
|
|
||||||
// Calling foo(String) with (String) is exact match
|
// Calling foo(String) with (String) is exact match
|
||||||
checkMatch(new Class<?>[] {String.class}, new Class<?>[] {String.class}, tc, ReflectionHelper.ArgumentsMatchKind.EXACT);
|
checkMatch(new Class<?>[] {String.class}, new Class<?>[] {String.class}, tc, EXACT);
|
||||||
|
|
||||||
// Calling foo(String,Integer) with (String,Integer) is exact match
|
// Calling foo(String,Integer) with (String,Integer) is exact match
|
||||||
checkMatch(new Class<?>[] {String.class, Integer.class}, new Class<?>[] {String.class, Integer.class}, tc, ArgumentsMatchKind.EXACT);
|
checkMatch(new Class<?>[] {String.class, Integer.class}, new Class<?>[] {String.class, Integer.class}, tc, EXACT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void reflectionHelperCompareArguments_CloseMatching() {
|
void reflectionHelperCompareArguments_CloseMatching() {
|
||||||
StandardTypeConverter tc = new StandardTypeConverter();
|
|
||||||
|
|
||||||
// Calling foo(List) with (ArrayList) is close match (no conversion required)
|
// Calling foo(List) with (ArrayList) is close match (no conversion required)
|
||||||
checkMatch(new Class<?>[] {ArrayList.class}, new Class<?>[] {List.class}, tc, ArgumentsMatchKind.CLOSE);
|
checkMatch(new Class<?>[] {ArrayList.class}, new Class<?>[] {List.class}, tc, CLOSE);
|
||||||
|
|
||||||
// Passing (Sub,String) on call to foo(Super,String) is close match
|
// Passing (Sub,String) on call to foo(Super,String) is close match
|
||||||
checkMatch(new Class<?>[] {Sub.class, String.class}, new Class<?>[] {Super.class, String.class}, tc, ArgumentsMatchKind.CLOSE);
|
checkMatch(new Class<?>[] {Sub.class, String.class}, new Class<?>[] {Super.class, String.class}, tc, CLOSE);
|
||||||
|
|
||||||
// Passing (String,Sub) on call to foo(String,Super) is close match
|
// Passing (String,Sub) on call to foo(String,Super) is close match
|
||||||
checkMatch(new Class<?>[] {String.class, Sub.class}, new Class<?>[] {String.class, Super.class}, tc, ArgumentsMatchKind.CLOSE);
|
checkMatch(new Class<?>[] {String.class, Sub.class}, new Class<?>[] {String.class, Super.class}, tc, CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void reflectionHelperCompareArguments_RequiresConversionMatching() {
|
void reflectionHelperCompareArguments_CloseMatching_WithAutoBoxing() {
|
||||||
StandardTypeConverter tc = new StandardTypeConverter();
|
|
||||||
|
|
||||||
// Calling foo(String,int) with (String,Integer) requires boxing conversion of argument one
|
// Calling foo(String,int) with (String,Integer) requires boxing conversion of argument one
|
||||||
checkMatch(new Class<?>[] {String.class, int.class}, new Class<?>[] {String.class,Integer.class},tc, ArgumentsMatchKind.CLOSE);
|
checkMatch(new Class<?>[] {String.class, int.class}, new Class<?>[] {String.class, Integer.class},tc, CLOSE);
|
||||||
|
|
||||||
// Passing (int,String) on call to foo(Integer,String) requires boxing conversion of argument zero
|
// Passing (int,String) on call to foo(Integer,String) requires boxing conversion of argument zero
|
||||||
checkMatch(new Class<?>[] {int.class, String.class}, new Class<?>[] {Integer.class, String.class},tc, ArgumentsMatchKind.CLOSE);
|
checkMatch(new Class<?>[] {int.class, String.class}, new Class<?>[] {Integer.class, String.class},tc, CLOSE);
|
||||||
|
|
||||||
// Passing (int,Sub) on call to foo(Integer,Super) requires boxing conversion of argument zero
|
// Passing (int,Sub) on call to foo(Integer,Super) requires boxing conversion of argument zero
|
||||||
checkMatch(new Class<?>[] {int.class, Sub.class}, new Class<?>[] {Integer.class, Super.class}, tc, ArgumentsMatchKind.CLOSE);
|
checkMatch(new Class<?>[] {int.class, Sub.class}, new Class<?>[] {Integer.class, Super.class}, tc, CLOSE);
|
||||||
|
|
||||||
// Passing (int,Sub,boolean) on call to foo(Integer,Super,Boolean) requires boxing conversion of arguments zero and two
|
// Passing (int,Sub,boolean) on call to foo(Integer,Super,Boolean) requires boxing conversion of arguments zero and two
|
||||||
// TODO checkMatch(new Class<?>[] {int.class, Sub.class, boolean.class}, new Class<?>[] {Integer.class, Super.class, Boolean.class}, tc, ArgsMatchKind.REQUIRES_CONVERSION);
|
checkMatch(new Class<?>[] {int.class, Sub.class, boolean.class}, new Class<?>[] {Integer.class, Super.class, Boolean.class}, tc, CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -149,59 +150,56 @@ class ReflectionHelperTests extends AbstractExpressionTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void reflectionHelperCompareArguments_Varargs_ExactMatching() {
|
void reflectionHelperCompareArguments_Varargs() {
|
||||||
StandardTypeConverter tc = new StandardTypeConverter();
|
|
||||||
|
|
||||||
// Passing (String[]) on call to (String[]) is exact match
|
// Passing (String[]) on call to (String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {String[].class}, new Class<?>[] {String[].class}, tc, ArgumentsMatchKind.EXACT);
|
checkMatchVarargs(new Class<?>[] {String[].class}, new Class<?>[] {String[].class}, tc, EXACT);
|
||||||
|
|
||||||
// Passing (Integer, String[]) on call to (Integer, String[]) is exact match
|
// Passing (Integer, String[]) on call to (Integer, String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {Integer.class, String[].class}, tc, ArgumentsMatchKind.EXACT);
|
checkMatchVarargs(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {Integer.class, String[].class}, tc, EXACT);
|
||||||
|
|
||||||
// Passing (String, Integer, String[]) on call to (String, String, String[]) is exact match
|
// Passing (String, Integer, String[]) on call to (String, String, String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {String.class, Integer.class, String[].class}, new Class<?>[] {String.class,Integer.class, String[].class}, tc, ArgumentsMatchKind.EXACT);
|
checkMatchVarargs(new Class<?>[] {String.class, Integer.class, String[].class}, new Class<?>[] {String.class,Integer.class, String[].class}, tc, EXACT);
|
||||||
|
|
||||||
// Passing (Sub, String[]) on call to (Super, String[]) is exact match
|
// Passing (Sub, String[]) on call to (Super, String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {Sub.class, String[].class}, new Class<?>[] {Super.class,String[].class}, tc, ArgumentsMatchKind.CLOSE);
|
checkMatchVarargs(new Class<?>[] {Sub.class, String[].class}, new Class<?>[] {Super.class,String[].class}, tc, CLOSE);
|
||||||
|
|
||||||
// Passing (Integer, String[]) on call to (String, String[]) is exact match
|
// Passing (Integer, String[]) on call to (String, String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {String.class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);
|
checkMatchVarargs(new Class<?>[] {Integer.class, String[].class}, new Class<?>[] {String.class, String[].class}, tc, REQUIRES_CONVERSION);
|
||||||
|
|
||||||
// Passing (Integer, Sub, String[]) on call to (String, Super, String[]) is exact match
|
// Passing (Integer, Sub, String[]) on call to (String, Super, String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {Integer.class, Sub.class, String[].class}, new Class<?>[] {String.class, Super.class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);
|
checkMatchVarargs(new Class<?>[] {Integer.class, Sub.class, String[].class}, new Class<?>[] {String.class, Super.class, String[].class}, tc, REQUIRES_CONVERSION);
|
||||||
|
|
||||||
// Passing (String) on call to (String[]) is exact match
|
// Passing (String) on call to (String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {String.class}, new Class<?>[] {String[].class}, tc, ArgumentsMatchKind.EXACT);
|
checkMatchVarargs(new Class<?>[] {String.class}, new Class<?>[] {String[].class}, tc, EXACT);
|
||||||
|
|
||||||
// Passing (Integer,String) on call to (Integer,String[]) is exact match
|
// Passing (Integer,String) on call to (Integer,String[]) is exact match
|
||||||
checkMatch2(new Class<?>[] {Integer.class, String.class}, new Class<?>[] {Integer.class, String[].class}, tc, ArgumentsMatchKind.EXACT);
|
checkMatchVarargs(new Class<?>[] {Integer.class, String.class}, new Class<?>[] {Integer.class, String[].class}, tc, EXACT);
|
||||||
|
|
||||||
// Passing (String) on call to (Integer[]) is conversion match (String to Integer)
|
// Passing (String) on call to (Integer[]) is conversion match (String to Integer)
|
||||||
checkMatch2(new Class<?>[] {String.class}, new Class<?>[] {Integer[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);
|
checkMatchVarargs(new Class<?>[] {String.class}, new Class<?>[] {Integer[].class}, tc, REQUIRES_CONVERSION);
|
||||||
|
|
||||||
// Passing (Sub) on call to (Super[]) is close match
|
// Passing (Sub) on call to (Super[]) is close match
|
||||||
checkMatch2(new Class<?>[] {Sub.class}, new Class<?>[] {Super[].class}, tc, ArgumentsMatchKind.CLOSE);
|
checkMatchVarargs(new Class<?>[] {Sub.class}, new Class<?>[] {Super[].class}, tc, CLOSE);
|
||||||
|
|
||||||
// Passing (Super) on call to (Sub[]) is not a match
|
// Passing (Super) on call to (Sub[]) is not a match
|
||||||
checkMatch2(new Class<?>[] {Super.class}, new Class<?>[] {Sub[].class}, tc, null);
|
checkMatchVarargs(new Class<?>[] {Super.class}, new Class<?>[] {Sub[].class}, tc, null);
|
||||||
|
|
||||||
checkMatch2(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null);
|
checkMatchVarargs(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null);
|
||||||
|
|
||||||
checkMatch2(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null);
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null);
|
||||||
|
|
||||||
checkMatch2(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null);
|
checkMatchVarargs(new Class<?>[] {Unconvertable.class, String.class}, new Class<?>[] {Sub.class, Super[].class}, tc, null);
|
||||||
|
|
||||||
checkMatch2(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null);
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, String.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, null);
|
||||||
|
|
||||||
checkMatch2(new Class<?>[] {Integer.class, Integer.class, Sub.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, Sub.class}, new Class<?>[] {String.class, String.class, Super[].class}, tc, REQUIRES_CONVERSION);
|
||||||
|
|
||||||
checkMatch2(new Class<?>[] {Integer.class, Integer.class, Integer.class}, new Class<?>[] {Integer.class, String[].class}, tc, ArgumentsMatchKind.REQUIRES_CONVERSION);
|
checkMatchVarargs(new Class<?>[] {Integer.class, Integer.class, Integer.class}, new Class<?>[] {Integer.class, String[].class}, tc, REQUIRES_CONVERSION);
|
||||||
// what happens on (Integer,String) passed to (Integer[]) ?
|
// what happens on (Integer,String) passed to (Integer[]) ?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void convertArguments() throws Exception {
|
void convertArguments() throws Exception {
|
||||||
StandardTypeConverter tc = new StandardTypeConverter();
|
|
||||||
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
|
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
|
||||||
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
|
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
|
||||||
|
|
||||||
|
@ -227,8 +225,7 @@ class ReflectionHelperTests extends AbstractExpressionTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void convertArguments2() throws Exception {
|
void convertAllArguments() throws Exception {
|
||||||
StandardTypeConverter tc = new StandardTypeConverter();
|
|
||||||
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
|
Method oneArg = TestInterface.class.getMethod("oneArg", String.class);
|
||||||
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
|
Method twoArg = TestInterface.class.getMethod("twoArg", String.class, String[].class);
|
||||||
|
|
||||||
|
@ -254,7 +251,7 @@ class ReflectionHelperTests extends AbstractExpressionTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void setupArguments() {
|
void setupArgumentsForVarargsInvocation() {
|
||||||
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
|
Object[] newArray = ReflectionHelper.setupArgumentsForVarargsInvocation(
|
||||||
new Class<?>[] {String[].class}, "a", "b", "c");
|
new Class<?>[] {String[].class}, "a", "b", "c");
|
||||||
|
|
||||||
|
@ -402,21 +399,21 @@ class ReflectionHelperTests extends AbstractExpressionTests {
|
||||||
assertThat(matchInfo).as("Should not be a null match").isNotNull();
|
assertThat(matchInfo).as("Should not be a null match").isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expectedMatchKind == ArgumentsMatchKind.EXACT) {
|
if (expectedMatchKind == EXACT) {
|
||||||
assertThat(matchInfo.isExactMatch()).isTrue();
|
assertThat(matchInfo.isExactMatch()).isTrue();
|
||||||
}
|
}
|
||||||
else if (expectedMatchKind == ArgumentsMatchKind.CLOSE) {
|
else if (expectedMatchKind == CLOSE) {
|
||||||
assertThat(matchInfo.isCloseMatch()).isTrue();
|
assertThat(matchInfo.isCloseMatch()).isTrue();
|
||||||
}
|
}
|
||||||
else if (expectedMatchKind == ArgumentsMatchKind.REQUIRES_CONVERSION) {
|
else if (expectedMatchKind == REQUIRES_CONVERSION) {
|
||||||
assertThat(matchInfo.isMatchRequiringConversion()).as("expected to be a match requiring conversion, but was " + matchInfo).isTrue();
|
assertThat(matchInfo.isMatchRequiringConversion()).as("expected to be a match requiring conversion, but was " + matchInfo).isTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to validate the match returned from a compareArguments call.
|
* Used to validate the match returned from a compareArgumentsVarargs call.
|
||||||
*/
|
*/
|
||||||
private static void checkMatch2(Class<?>[] inputTypes, Class<?>[] expectedTypes,
|
private static void checkMatchVarargs(Class<?>[] inputTypes, Class<?>[] expectedTypes,
|
||||||
StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) {
|
StandardTypeConverter typeConverter, ArgumentsMatchKind expectedMatchKind) {
|
||||||
|
|
||||||
ReflectionHelper.ArgumentsMatchInfo matchInfo =
|
ReflectionHelper.ArgumentsMatchInfo matchInfo =
|
||||||
|
@ -438,14 +435,10 @@ class ReflectionHelperTests extends AbstractExpressionTests {
|
||||||
private static void checkArguments(Object[] args, Object... expected) {
|
private static void checkArguments(Object[] args, Object... expected) {
|
||||||
assertThat(args).hasSize(expected.length);
|
assertThat(args).hasSize(expected.length);
|
||||||
for (int i = 0; i < expected.length; i++) {
|
for (int i = 0; i < expected.length; i++) {
|
||||||
checkArgument(expected[i], args[i]);
|
assertThat(args[i]).isEqualTo(expected[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkArgument(Object expected, Object actual) {
|
|
||||||
assertThat(actual).isEqualTo(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<TypeDescriptor> typeDescriptors(Class<?>... types) {
|
private static List<TypeDescriptor> typeDescriptors(Class<?>... types) {
|
||||||
return Arrays.stream(types).map(TypeDescriptor::valueOf).toList();
|
return Arrays.stream(types).map(TypeDescriptor::valueOf).toList();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue