Modify SpEL Tokenizer to support methods on numbers
When attempting to parse an Integer literal expression such as 42.toString(), SpEL currently throws a SpelParseException with a message similar to: "EL1041E:(pos 3): After parsing a valid expression, there is still more data in the expression: 'toString'". The problem here is that '3.' is currently considered a valid number (including the dot). However, SpEL succeeds at parsing an equivalent expression for a Double literal such as 3.14.isInfinite(). To address this issue, the SpEL Tokenizer no longer consumes the trailing '.' on an integer as part of the integer. So '3.foo()' will now be parsed as '3' '.' 'foo()' and not '3.' 'foo()' -- which was what prevented parsing of method invocations on integers. To keep the change simple, the parser will no longer handle real numbers of the form '3.e4'. From now on they must include the extra 0 (i.e., '3.0e4'). Issue: SPR-9612
This commit is contained in:
committed by
Sam Brannen
parent
015086cb9c
commit
e4a926ea3c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,9 +30,10 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A SpelExpressions represents a parsed (valid) expression that is ready to be evaluated in a specified context. An
|
||||
* expression can be evaluated standalone or in a specified context. During expression evaluation the context may be
|
||||
* asked to resolve references to types, beans, properties, methods.
|
||||
* A {@code SpelExpression} represents a parsed (valid) expression that is ready
|
||||
* to be evaluated in a specified context. An expression can be evaluated
|
||||
* standalone or in a specified context. During expression evaluation the context
|
||||
* may be asked to resolve references to types, beans, properties, and methods.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
@@ -103,22 +104,22 @@ public class SpelExpression implements Expression {
|
||||
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
|
||||
}
|
||||
|
||||
public Class getValueType() throws EvaluationException {
|
||||
public Class<?> getValueType() throws EvaluationException {
|
||||
return getValueType(getEvaluationContext());
|
||||
}
|
||||
|
||||
public Class getValueType(Object rootObject) throws EvaluationException {
|
||||
public Class<?> getValueType(Object rootObject) throws EvaluationException {
|
||||
return getValueType(getEvaluationContext(), rootObject);
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context) throws EvaluationException {
|
||||
public Class<?> getValueType(EvaluationContext context) throws EvaluationException {
|
||||
Assert.notNull(context, "The EvaluationContext is required");
|
||||
ExpressionState eState = new ExpressionState(context, configuration);
|
||||
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
|
||||
return typeDescriptor != null ? typeDescriptor.getType() : null;
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
public Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration);
|
||||
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
|
||||
return typeDescriptor != null ? typeDescriptor.getType() : null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -289,10 +289,19 @@ class Tokenizer {
|
||||
ch = toProcess[pos];
|
||||
if (ch=='.') {
|
||||
isReal = true;
|
||||
int dotpos = pos;
|
||||
// carry on consuming digits
|
||||
do {
|
||||
pos++;
|
||||
} while (isDigit(toProcess[pos]));
|
||||
if (pos == dotpos + 1) {
|
||||
// the number is something like '3.'. It is really an int but may be
|
||||
// part of something like '3.toString()'. In this case process it as
|
||||
// an int and leave the dot as a separate token.
|
||||
pos = dotpos;
|
||||
pushIntToken(subarray(start, pos), false, start, pos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int endOfNumber = pos;
|
||||
@@ -307,7 +316,7 @@ class Tokenizer {
|
||||
pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber);
|
||||
pos++;
|
||||
} else if (isExponentChar(toProcess[pos])) {
|
||||
isReal = true; // if it wasnt before, it is now
|
||||
isReal = true; // if it wasn't before, it is now
|
||||
pos++;
|
||||
char possibleSign = toProcess[pos];
|
||||
if (isSign(possibleSign)) {
|
||||
@@ -502,6 +511,5 @@ class Tokenizer {
|
||||
flags[ch]|= IS_ALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user