More tests. First pass on messages review.
This commit is contained in:
@@ -30,8 +30,7 @@ import java.text.MessageFormat;
|
||||
* EL1004E: (pos 34): Type cannot be found 'String'
|
||||
* </pre>
|
||||
*
|
||||
* </code> The prefix captures the code and the error kind, whilst the position is included if it is known and the
|
||||
* message has had all relevant inserts applied to it.
|
||||
* </code> The prefix captures the code and the error kind, whilst the position is included if it is known.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
@@ -41,82 +40,66 @@ public enum SpelMessages {
|
||||
// TODO review if any messages are not used
|
||||
// TODO sort messages into better groups if possible, sharing a name prefix perhaps
|
||||
|
||||
INITIALIZER_LENGTH_INCORRECT(Kind.ERROR, 1001,
|
||||
"Array constructor call: initializer size of {0} does not match declared length of {1}"), TYPE_CONVERSION_ERROR(
|
||||
Kind.ERROR, 1002, "Type conversion problem, cannot convert from {0} to {1}"), CONSTRUCTOR_NOT_FOUND(
|
||||
Kind.ERROR, 1003, "Constructor call: No suitable constructor on type {0} for arguments {1}"), TYPE_NOT_FOUND(
|
||||
Kind.ERROR, 1004, "Type cannot be found ''{0}''"), ADDITION_NOT_DEFINED(Kind.ERROR, 1005,
|
||||
"Addition not defined between operands of type {0} and {1}"), METHOD_NOT_FOUND(Kind.ERROR, 1006,
|
||||
"Method call: Method {0} cannot be found on {1} type"), ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT(
|
||||
Kind.ERROR, 1007, "Method call: Attempted to call method {0} on null context object"), ATTEMPTED_PROPERTY_FIELD_REF_ON_NULL_CONTEXT_OBJECT(
|
||||
Kind.ERROR, 1008,
|
||||
"Field or property reference: Attempted to refer to field or property ''{0}'' on null context object"), PROPERTY_OR_FIELD_NOT_FOUND(
|
||||
Kind.ERROR, 1009, "Field or property ''{0}'' cannot be found on object of type ''{1}''"), PROPERTY_OR_FIELD_SETTER_NOT_FOUND(
|
||||
Kind.ERROR, 1010, "Field or property ''{0}'' cannot be set on object of type ''{1}''"), MULTIPLY_NOT_DEFINED(
|
||||
Kind.ERROR, 1011, "Multiply not defined between operands of type {0} and {1}"), NOT_COMPARABLE(Kind.ERROR,
|
||||
1012, "Cannot compare instances of {0} and {1}"), NOT_COMPARABLE_CANNOT_COERCE(Kind.ERROR, 1013,
|
||||
"Cannot compare instances of {0} and {1} because they cannot be coerced to the same type"), VARIABLE_NOT_FOUND(
|
||||
Kind.ERROR, 1014, "Variable named ''{0}'' cannot be found"), INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(
|
||||
Kind.ERROR, 1015, "Incorrect number of arguments for function, {0} supplied but function takes {1}"), NO_SUCH_FUNCTION(
|
||||
Kind.ERROR, 1016, "No such function named ''{0}''"), NOT_A_FUNCTION(Kind.ERROR, 1017,
|
||||
"The name ''{0}'' did not map to a function, it mapped to a ''{1}''"), INVALID_TYPE_FOR_SELECTION(
|
||||
Kind.ERROR, 1018, "Cannot perform selection on input data of type ''{0}''"), RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(
|
||||
Kind.ERROR, 1019, "Result of selection criteria is not boolean"), MODULUS_NOT_DEFINED(Kind.ERROR, 1020,
|
||||
"Modulus not defined between operands of type ''{0}'' and ''{1}''"), NULL_OPERAND_TO_OPERATOR(Kind.ERROR,
|
||||
1021, "Operand evaluated to null and that is not supported for this operator"), NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION(
|
||||
Kind.ERROR, 1022, "No array size or initializer was supplied to construct the array"), INCORRECT_ELEMENT_TYPE_FOR_ARRAY(
|
||||
Kind.ERROR, 1023, "The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"), BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(
|
||||
Kind.ERROR, 1024, "Right operand for the 'between' operator has to be a two-element list"), TYPE_NOT_SUPPORTED_BY_PROCESSOR(
|
||||
Kind.ERROR, 1025,
|
||||
"The collection processor ''{0}'' does not understand and input collection of elements of type {1}"), UNABLE_TO_ACCESS_FIELD(
|
||||
Kind.ERROR, 1026, "Unable to access field ''{0}'' on type ''{1}''"), UNABLE_TO_ACCESS_PROPERTY_THROUGH_GETTER(
|
||||
Kind.ERROR, 1027, "Unable to access property ''{0}'' through getter on type ''{1}''"), UNABLE_TO_ACCESS_PROPERTY_THROUGH_SETTER(
|
||||
Kind.ERROR, 1028, "Unable to access property ''{0}'' through setter on type ''{1}''"), INVALID_PATTERN(
|
||||
Kind.ERROR, 1029, "Pattern is not valid ''{0}''"), RECOGNITION_ERROR(Kind.ERROR, 1030,
|
||||
"Recognition error: {0}"), // TODO poor message when a recognition exception occurs
|
||||
PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1031, "Projection is not supported on the type ''{0}''"), ARGLIST_SHOULD_NOT_BE_EVALUATED(
|
||||
Kind.ERROR, 1032, "The argument list of a lambda expression should never have getValue() called upon it"), MAPENTRY_SHOULD_NOT_BE_EVALUATED(
|
||||
Kind.ERROR, 1033, "A map entry should never have getValue() called upon it"), EXCEPTION_DURING_PROPERTY_READ(
|
||||
Kind.ERROR, 1034, "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"), EXCEPTION_DURING_CONSTRUCTOR_INVOCATION(
|
||||
Kind.ERROR, 1035, "A problem occurred whilst attempting to construct ''{0}'': ''{1}''"), DATE_CANNOT_BE_PARSED(
|
||||
Kind.ERROR, 1036, "Unable to parse date ''{0}'' using format ''{1}''"), FUNCTION_REFERENCE_CANNOT_BE_INVOKED(
|
||||
Kind.ERROR, 1037, "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"), FUNCTION_NOT_DEFINED(
|
||||
Kind.ERROR, 1038, "The function ''{0}'' could not be found"), EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR,
|
||||
1039, "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"), ARRAY_INDEX_OUT_OF_BOUNDS(
|
||||
Kind.ERROR, 1040, "The array has ''{0}'' elements, index ''{1}'' is invalid"), COLLECTION_INDEX_OUT_OF_BOUNDS(
|
||||
Kind.ERROR, 1041, "The collection has ''{0}'' elements, index ''{1}'' is invalid"), STRING_INDEX_OUT_OF_BOUNDS(
|
||||
Kind.ERROR, 1042, "The string has ''{0}'' characters, index ''{1}'' is invalid"), INDEXING_NOT_SUPPORTED_FOR_TYPE(
|
||||
Kind.ERROR, 1043, "Indexing into type ''{0}'' is not supported"), OPERATOR_IN_CANNOT_DETERMINE_MEMBERSHIP(
|
||||
Kind.ERROR, 1044, "Operator 'in' not implemented for detecting membership of a ''{0}'' in a ''{1}''"), CANNOT_NEGATE_TYPE(
|
||||
Kind.ERROR, 1045, "Cannot determine negation of type ''{0}''"), CUT_ARGUMENTS_MUST_BE_INTS(Kind.ERROR,
|
||||
1046, "Both arguments to the cut() processor must be Integers, but they are ''{0}'' and ''{1}''"), SOUNDSLIKE_NEEDS_STRING_OPERAND(
|
||||
Kind.ERROR, 1047, "The soundslike operator needs String operands, but found a ''{0}''"), INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(
|
||||
Kind.ERROR, 1048, "The operator 'instanceof' needs the right operand to be a class, not a ''{0}''"), LOCAL_VARIABLE_NOT_DEFINED(
|
||||
Kind.ERROR, 1049, "Local variable named ''{0}'' could not be found"), EXCEPTION_DURING_METHOD_INVOCATION(
|
||||
Kind.ERROR, 1050,
|
||||
"A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"), PLACEHOLDER_SHOULD_NEVER_BE_EVALUATED(
|
||||
Kind.ERROR, 1051, "InternalError: A placeholder node in the Ast should never be evaluated!"), OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(
|
||||
Kind.ERROR, 1052, "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"), UNEXPECTED_PROBLEM_INVOKING_OPERATOR(
|
||||
Kind.ERROR, 1054,
|
||||
"Unexpected problem invoking operator ''{0}'' between objects of type ''{1}'' and ''{2}'': {3}"), PROBLEM_LOCATING_METHOD(
|
||||
Kind.ERROR, 1055, "Problem locating method {0} cannot on type {1}"), PROBLEM_LOCATING_CONSTRUCTOR(
|
||||
Kind.ERROR, 1056,
|
||||
"A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"), INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR(
|
||||
Kind.ERROR, 1057, "First operand to like operator must be a string. ''{0}'' is not"), INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR(
|
||||
Kind.ERROR, 1058, "Second operand to like operator must be a string (regex). ''{0}'' is not"), SETVALUE_NOT_SUPPORTED(
|
||||
Kind.ERROR, 1059, "setValue(ExpressionState, Object) not implemented for ''{0}'' (''{1}''"), TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(
|
||||
Kind.ERROR, 1060, "Expected the type of the new array to be specified as a String but found ''{0}''"), PROBLEM_DURING_TYPE_CONVERSION(
|
||||
Kind.ERROR, 1061, "Problem occurred during type conversion: {0}"), MULTIPLE_POSSIBLE_METHODS(Kind.ERROR,
|
||||
1062, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"), EXCEPTION_DURING_PROPERTY_WRITE(
|
||||
Kind.ERROR, 1063, "A problem occurred whilst attempting to set the property ''{0}'': {1}"), NOT_AN_INTEGER(
|
||||
Kind.ERROR, 1064, "The value ''{0}'' cannot be parsed as an int"), NOT_A_LONG(Kind.ERROR, 1065,
|
||||
"The value ''{0}'' cannot be parsed as a long"), PARSE_PROBLEM(Kind.ERROR, 1066,
|
||||
"Error occurred during expression parse: {0}"), INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR,
|
||||
1067, "First operand to matches operator must be a string. ''{0}'' is not"), INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(
|
||||
Kind.ERROR, 1068, "Second operand to matches operator must be a string. ''{0}'' is not"), //
|
||||
FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1069,
|
||||
"Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static."),//
|
||||
CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1070, "Cannot index into a null value");
|
||||
TYPE_CONVERSION_ERROR(Kind.ERROR, 1001, "Type conversion problem, cannot convert from {0} to {1}"), //
|
||||
CONSTRUCTOR_NOT_FOUND(Kind.ERROR, 1002, "Constructor call: No suitable constructor found on type {0} for arguments {1}"), //
|
||||
METHOD_NOT_FOUND(Kind.ERROR, 1003, "Method call: Method {0} cannot be found on {1} type"), //
|
||||
TYPE_NOT_FOUND(Kind.ERROR, 1004, "Type cannot be found ''{0}''"), //
|
||||
VARIABLE_NOT_FOUND(Kind.ERROR, 1005, "Variable named ''{0}'' cannot be found"), //
|
||||
LOCAL_VARIABLE_NOT_DEFINED(Kind.ERROR, 1006, "Local variable named ''{0}'' could not be found"), //
|
||||
FUNCTION_NOT_DEFINED(Kind.ERROR, 1007, "The function ''{0}'' could not be found"), //
|
||||
PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL(Kind.ERROR, 1008, "Field or property ''{0}'' cannot be found on null"), //
|
||||
PROPERTY_OR_FIELD_NOT_READABLE(Kind.ERROR, 1009, "Field or property ''{0}'' cannot be found on object of type ''{1}''"), //
|
||||
PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL(Kind.ERROR, 1010, "Field or property ''{0}'' cannot be set on null"), //
|
||||
PROPERTY_OR_FIELD_NOT_WRITABLE(Kind.ERROR, 1010, "Field or property ''{0}'' cannot be set on object of type ''{1}''"), //
|
||||
|
||||
METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1011, "Method call: Attempted to call method {0} on null context object"), //
|
||||
PROPERTY_OR_FIELD_ACCESS_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1012, "Field or property reference: Attempted to refer to field or property ''{0}'' on null context object"), //
|
||||
CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1013, "Cannot index into a null value"),
|
||||
|
||||
NOT_COMPARABLE(Kind.ERROR, 1014, "Cannot compare instances of {0} and {1}"), //
|
||||
NOT_COMPARABLE_CANNOT_COERCE(Kind.ERROR, 1015, "Cannot compare instances of {0} and {1} because they cannot be coerced to the same type"), //
|
||||
INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(Kind.ERROR, 1016, "Incorrect number of arguments for function, {0} supplied but function takes {1}"), //
|
||||
INVALID_TYPE_FOR_SELECTION(Kind.ERROR, 1017, "Cannot perform selection on input data of type ''{0}''"), //
|
||||
RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(Kind.ERROR, 1018, "Result of selection criteria is not boolean"), //
|
||||
NULL_OPERAND_TO_OPERATOR(Kind.ERROR, 1019, "Operand evaluated to null and that is not supported for this operator"), //
|
||||
BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(Kind.ERROR, 1020, "Right operand for the 'between' operator has to be a two-element list"), //
|
||||
UNABLE_TO_ACCESS_FIELD(Kind.ERROR, 1021, "Unable to access field ''{0}'' on type ''{1}''"), //
|
||||
UNABLE_TO_ACCESS_PROPERTY_THROUGH_GETTER(Kind.ERROR, 1022, "Unable to access property ''{0}'' through getter on type ''{1}''"), //
|
||||
UNABLE_TO_ACCESS_PROPERTY_THROUGH_SETTER(Kind.ERROR, 1023, "Unable to access property ''{0}'' through setter on type ''{1}''"), //
|
||||
INVALID_PATTERN(Kind.ERROR, 1024, "Pattern is not valid ''{0}''"), //
|
||||
RECOGNITION_ERROR(Kind.ERROR, 1025, "Recognition error: {0}"), // TODO poor message when a recognition exception occurs
|
||||
PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1026, "Projection is not supported on the type ''{0}''"), //
|
||||
ARGLIST_SHOULD_NOT_BE_EVALUATED(Kind.ERROR, 1027, "The argument list of a lambda expression should never have getValue() called upon it"), //
|
||||
MAPENTRY_SHOULD_NOT_BE_EVALUATED(Kind.ERROR, 1028, "A map entry should never have getValue() called upon it"), //
|
||||
EXCEPTION_DURING_PROPERTY_READ(Kind.ERROR, 1029, "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"), //
|
||||
EXCEPTION_DURING_CONSTRUCTOR_INVOCATION(Kind.ERROR, 1030, "A problem occurred whilst attempting to construct ''{0}'': ''{1}''"), //
|
||||
DATE_CANNOT_BE_PARSED(Kind.ERROR, 1031, "Unable to parse date ''{0}'' using format ''{1}''"), //
|
||||
FUNCTION_REFERENCE_CANNOT_BE_INVOKED(Kind.ERROR, 1032, "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"), //
|
||||
EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR, 1033, "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"), //
|
||||
|
||||
// indexing
|
||||
ARRAY_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1034, "The array has ''{0}'' elements, index ''{1}'' is invalid"), //
|
||||
COLLECTION_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1035, "The collection has ''{0}'' elements, index ''{1}'' is invalid"), //
|
||||
STRING_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1036, "The string has ''{0}'' characters, index ''{1}'' is invalid"), //
|
||||
INDEXING_NOT_SUPPORTED_FOR_TYPE(Kind.ERROR, 1037, "Indexing into type ''{0}'' is not supported"), //
|
||||
|
||||
INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(Kind.ERROR, 1038, "The operator 'instanceof' needs the right operand to be a class, not a ''{0}''"), //
|
||||
EXCEPTION_DURING_METHOD_INVOCATION(Kind.ERROR, 1039, "A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"), //
|
||||
OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(Kind.ERROR, 1040, "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"), //
|
||||
UNEXPECTED_PROBLEM_INVOKING_OPERATOR(Kind.ERROR, 1041, "Unexpected problem invoking operator ''{0}'' between objects of type ''{1}'' and ''{2}'': {3}"), //
|
||||
PROBLEM_LOCATING_METHOD(Kind.ERROR, 1042, "Problem locating method {0} cannot on type {1}"),
|
||||
PROBLEM_LOCATING_CONSTRUCTOR(Kind.ERROR, 1043, "A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"), //
|
||||
SETVALUE_NOT_SUPPORTED( Kind.ERROR, 1044, "setValue(ExpressionState, Object) not implemented for ''{0}'' (''{1}''"), //
|
||||
PROBLEM_DURING_TYPE_CONVERSION(Kind.ERROR, 1045, "Problem occurred during type conversion: {0}"), //
|
||||
MULTIPLE_POSSIBLE_METHODS(Kind.ERROR, 1046, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"), //
|
||||
EXCEPTION_DURING_PROPERTY_WRITE(Kind.ERROR, 1047, "A problem occurred whilst attempting to set the property ''{0}'': {1}"), //
|
||||
NOT_AN_INTEGER(Kind.ERROR, 1048, "The value ''{0}'' cannot be parsed as an int"), //
|
||||
NOT_A_LONG(Kind.ERROR, 1049, "The value ''{0}'' cannot be parsed as a long"), //
|
||||
PARSE_PROBLEM(Kind.ERROR, 1050, "Error occurred during expression parse: {0}"), //
|
||||
INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1051, "First operand to matches operator must be a string. ''{0}'' is not"), //
|
||||
INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1052, "Second operand to matches operator must be a string. ''{0}'' is not"), //
|
||||
FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1053, "Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static."),//
|
||||
;
|
||||
|
||||
private Kind kind;
|
||||
private int code;
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.expression.spel.antlr;
|
||||
import org.antlr.runtime.ANTLRStringStream;
|
||||
import org.antlr.runtime.CommonTokenStream;
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.ParserContext;
|
||||
@@ -67,12 +66,10 @@ public class SpelAntlrExpressionParser extends TemplateAwareExpressionParser {
|
||||
this.parser.setTokenStream(tokens);
|
||||
expr_return exprReturn = this.parser.expr();
|
||||
return new SpelExpression(expressionString, (SpelNode) exprReturn.getTree());
|
||||
}
|
||||
catch (RecognitionException re) {
|
||||
} catch (RecognitionException re) {
|
||||
throw new ParseException(expressionString,
|
||||
"Recognition error at position: " + re.charPositionInLine + ": " + re.getMessage(), re);
|
||||
}
|
||||
catch (WrappedSpelException ex) {
|
||||
} catch (WrappedSpelException ex) {
|
||||
SpelException wrappedException = ex.getCause();
|
||||
throw new ParseException(expressionString,
|
||||
"Parsing problem: " + wrappedException.getMessage(), wrappedException);
|
||||
|
||||
@@ -49,7 +49,7 @@ public class ConstructorReference extends SpelNodeImpl {
|
||||
/**
|
||||
* The cached executor that may be reused on subsequent evaluations.
|
||||
*/
|
||||
private ConstructorExecutor cachedExecutor;
|
||||
private volatile ConstructorExecutor cachedExecutor;
|
||||
|
||||
public ConstructorReference(Token payload) {
|
||||
super(payload);
|
||||
@@ -112,7 +112,7 @@ public class ConstructorReference extends SpelNodeImpl {
|
||||
* @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
|
||||
* @throws SpelException if there is a problem locating the constructor
|
||||
*/
|
||||
protected ConstructorExecutor findExecutorForConstructor(
|
||||
private ConstructorExecutor findExecutorForConstructor(
|
||||
String typename, Class<?>[] argumentTypes, ExpressionState state) throws SpelException {
|
||||
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
|
||||
@@ -55,7 +55,7 @@ public class MethodReference extends SpelNodeImpl {
|
||||
arguments[i] = getChild(i).getValueInternal(state).getValue();
|
||||
}
|
||||
if (currentContext.getValue() == null) {
|
||||
throw new SpelException(getCharPositionInLine(), SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT,
|
||||
throw new SpelException(getCharPositionInLine(), SpelMessages.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED,
|
||||
FormatHelper.formatMethodForMessage(name, getTypes(arguments)));
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public class MethodReference extends SpelNodeImpl {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected MethodExecutor findAccessorForMethod(String name, Class<?>[] argumentTypes, ExpressionState state)
|
||||
private MethodExecutor findAccessorForMethod(String name, Class<?>[] argumentTypes, ExpressionState state)
|
||||
throws SpelException {
|
||||
|
||||
TypedValue context = state.getActiveContextObject();
|
||||
|
||||
@@ -111,8 +111,12 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage());
|
||||
}
|
||||
}
|
||||
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, name,
|
||||
FormatHelper.formatClassNameForMessage(contextObjectClass));
|
||||
if (contextObject.getValue() == null) {
|
||||
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL, name);
|
||||
} else {
|
||||
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_READABLE, name,
|
||||
FormatHelper.formatClassNameForMessage(contextObjectClass));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeProperty(ExpressionState state, String name, Object newValue) throws SpelException {
|
||||
@@ -149,18 +153,20 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
name, ae.getMessage());
|
||||
}
|
||||
}
|
||||
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND, name, FormatHelper
|
||||
.formatClassNameForMessage(contextObjectClass));
|
||||
if (contextObject.getValue()==null) {
|
||||
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL, name);
|
||||
} else {
|
||||
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_WRITABLE, name, FormatHelper
|
||||
.formatClassNameForMessage(contextObjectClass));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWritableProperty(String name, ExpressionState state) throws SpelException {
|
||||
Object contextObject = state.getActiveContextObject().getValue();
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
if (contextObject == null) {
|
||||
throw new SpelException(SpelMessages.ATTEMPTED_PROPERTY_FIELD_REF_ON_NULL_CONTEXT_OBJECT, name);
|
||||
}
|
||||
List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(
|
||||
(contextObject instanceof Class) ? ((Class<?>) contextObject) : contextObject.getClass(), state);
|
||||
|
||||
List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(getObjectClass(contextObject),state);
|
||||
|
||||
if (resolversToTry != null) {
|
||||
for (PropertyAccessor pfResolver : resolversToTry) {
|
||||
try {
|
||||
|
||||
@@ -49,8 +49,7 @@ public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Seria
|
||||
public final Object getValue(ExpressionState expressionState) throws EvaluationException {
|
||||
if (expressionState != null) {
|
||||
return getValueInternal(expressionState).getValue();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return getValue(new ExpressionState(new StandardEvaluationContext()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,9 @@ public class ReflectionHelper {
|
||||
* @param expectedArgTypes the array of types the method/constructor is expecting
|
||||
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
|
||||
* @param typeConverter a registered type converter
|
||||
* @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can
|
||||
* match an expected type
|
||||
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
|
||||
*/
|
||||
static ArgumentsMatchInfo compareArguments(
|
||||
public static ArgumentsMatchInfo compareArguments(
|
||||
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) {
|
||||
|
||||
ArgsMatchKind match = ArgsMatchKind.EXACT;
|
||||
@@ -58,6 +56,59 @@ public class ReflectionHelper {
|
||||
if (expectedArg != suppliedArg) {
|
||||
if (ClassUtils.isAssignable(expectedArg, suppliedArg)
|
||||
/* || isWidenableTo(expectedArg, suppliedArg) */) {
|
||||
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
match = ArgsMatchKind.CLOSE;
|
||||
}
|
||||
} else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
|
||||
if (argsRequiringConversion == null) {
|
||||
argsRequiringConversion = new ArrayList<Integer>();
|
||||
}
|
||||
argsRequiringConversion.add(i);
|
||||
match = ArgsMatchKind.REQUIRES_CONVERSION;
|
||||
} else {
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match == null) {
|
||||
return null;
|
||||
} else {
|
||||
if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
int[] argsArray = new int[argsRequiringConversion.size()];
|
||||
for (int i = 0; i < argsRequiringConversion.size(); i++) {
|
||||
argsArray[i] = argsRequiringConversion.get(i);
|
||||
}
|
||||
return new ArgumentsMatchInfo(match, argsArray);
|
||||
} else {
|
||||
return new ArgumentsMatchInfo(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare argument arrays and return information about whether they match. A supplied type converter and
|
||||
* conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
|
||||
* type by the converter. This variant of compareArguments also allows for a varargs match.
|
||||
* @param expectedArgTypes the array of types the method/constructor is expecting
|
||||
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
|
||||
* @param typeConverter a registered type converter
|
||||
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
|
||||
*/
|
||||
static ArgumentsMatchInfo compareArgumentsVarargs(
|
||||
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) {
|
||||
|
||||
ArgsMatchKind match = ArgsMatchKind.EXACT;
|
||||
List<Integer> argsRequiringConversion = null;
|
||||
|
||||
// Check up until the varargs argument:
|
||||
|
||||
// Deal with the arguments up to 'expected number' - 1 (that is everything but the varargs argument)
|
||||
int argCountUpToVarargs = expectedArgTypes.length-1;
|
||||
for (int i = 0; i < argCountUpToVarargs && match != null; i++) {
|
||||
Class suppliedArg = suppliedArgTypes[i];
|
||||
Class expectedArg = expectedArgTypes[i];
|
||||
if (expectedArg != suppliedArg) {
|
||||
if (expectedArg.isAssignableFrom(suppliedArg) || ClassUtils.isAssignableValue(expectedArg, suppliedArg)) {
|
||||
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
match = ArgsMatchKind.CLOSE;
|
||||
}
|
||||
@@ -72,64 +123,7 @@ public class ReflectionHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
int[] argsArray = new int[argsRequiringConversion.size()];
|
||||
for (int i = 0; i < argsRequiringConversion.size(); i++) {
|
||||
argsArray[i] = argsRequiringConversion.get(i);
|
||||
}
|
||||
return new ArgumentsMatchInfo(match, argsArray);
|
||||
}
|
||||
else {
|
||||
return new ArgumentsMatchInfo(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare argument arrays and return information about whether they match. A supplied type converter and
|
||||
* conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
|
||||
* type by the converter. This variant of compareArguments allows for a varargs match.
|
||||
* @param expectedArgTypes the array of types the method/constructor is expecting
|
||||
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
|
||||
* @param typeConverter a registered type converter
|
||||
* @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can
|
||||
* match an expected type
|
||||
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
|
||||
*/
|
||||
static ArgumentsMatchInfo compareArgumentsVarargs(
|
||||
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) {
|
||||
|
||||
ArgsMatchKind match = ArgsMatchKind.EXACT;
|
||||
List<Integer> argsRequiringConversion = null;
|
||||
|
||||
// Check up until the varargs argument:
|
||||
|
||||
// Deal with the arguments up to 'expected number' - 1
|
||||
for (int i = 0; i < expectedArgTypes.length - 1 && match != null; i++) {
|
||||
Class suppliedArg = suppliedArgTypes[i];
|
||||
Class expectedArg = expectedArgTypes[i];
|
||||
if (expectedArg != suppliedArg) {
|
||||
if (expectedArg.isAssignableFrom(suppliedArg) || ClassUtils.isAssignableValue(expectedArg, suppliedArg)) {
|
||||
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
match = ArgsMatchKind.CLOSE;
|
||||
}
|
||||
}
|
||||
else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
|
||||
if (argsRequiringConversion == null) {
|
||||
argsRequiringConversion = new ArrayList<Integer>();
|
||||
}
|
||||
argsRequiringConversion.add(i);
|
||||
match = ArgsMatchKind.REQUIRES_CONVERSION;
|
||||
} else {
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Already does not match
|
||||
// If already confirmed it cannot be a match, then returnW
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -138,9 +132,7 @@ public class ReflectionHelper {
|
||||
// that is a match, the caller has already built the array
|
||||
if (suppliedArgTypes.length == expectedArgTypes.length
|
||||
&& expectedArgTypes[expectedArgTypes.length - 1] == suppliedArgTypes[suppliedArgTypes.length - 1]) {
|
||||
|
||||
} else {
|
||||
|
||||
// Now... we have the final argument in the method we are checking as a match and we have 0 or more other
|
||||
// arguments left to pass to it.
|
||||
Class varargsParameterType = expectedArgTypes[expectedArgTypes.length - 1].getComponentType();
|
||||
@@ -185,7 +177,7 @@ public class ReflectionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
|
||||
public static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
|
||||
int[] argsRequiringConversion, Object... arguments) throws EvaluationException {
|
||||
Class varargsType = null;
|
||||
if (isVarargs) {
|
||||
@@ -219,8 +211,7 @@ public class ReflectionHelper {
|
||||
Class<?> targetType = null;
|
||||
if (isVarargs && i >= (parameterTypes.length - 1)) {
|
||||
targetType = varargsType;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
targetType = parameterTypes[i];
|
||||
}
|
||||
if (converter == null) {
|
||||
@@ -284,8 +275,7 @@ public class ReflectionHelper {
|
||||
}
|
||||
|
||||
|
||||
static enum ArgsMatchKind {
|
||||
|
||||
public static enum ArgsMatchKind {
|
||||
EXACT, CLOSE, REQUIRES_CONVERSION
|
||||
}
|
||||
|
||||
@@ -296,7 +286,7 @@ public class ReflectionHelper {
|
||||
* indicates that conversion is required for some of the arguments then the arguments that require conversion are
|
||||
* listed in the argsRequiringConversion array.
|
||||
*/
|
||||
static class ArgumentsMatchInfo {
|
||||
public static class ArgumentsMatchInfo {
|
||||
|
||||
public ArgsMatchKind kind;
|
||||
|
||||
@@ -310,6 +300,34 @@ public class ReflectionHelper {
|
||||
ArgumentsMatchInfo(ArgsMatchKind kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public boolean isExactMatch() {
|
||||
return kind==ArgsMatchKind.EXACT;
|
||||
}
|
||||
|
||||
public boolean isCloseMatch() {
|
||||
return kind==ArgsMatchKind.CLOSE;
|
||||
}
|
||||
|
||||
public boolean isMatchRequiringConversion() {
|
||||
return kind==ArgsMatchKind.REQUIRES_CONVERSION;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append("ArgumentMatch: ").append(kind);
|
||||
if (argsRequiringConversion!=null) {
|
||||
sb.append(" (argsForConversion:");
|
||||
for (int i=0;i<argsRequiringConversion.length;i++) {
|
||||
if (i>0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(argsRequiringConversion[i]);
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user