提交 e4a926ea 编写于 作者: A Andy Clement 提交者: Sam Brannen

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
上级 015086cb
/*
* 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;
......
/*
* 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;
}
}
}
......@@ -8,6 +8,7 @@ Changes in version 3.2 M2 (2012-08-xx)
* spring-test module now depends on junit:junit-dep (SPR-6966)
* now inferring return type of parameterized factory methods (SPR-9493)
* SpEL Tokenizer now supports methods on integers (SPR-9612)
* now using BufferedInputStream in SimpleMetaDataReader to double performance (SPR-9528)
* introduced "repeatCount" property in Quartz SimpleTriggerFactoryBean (SPR-9521)
* introduced "jtaTransactionManager" property in Hibernate 4 LocalSessionFactoryBean/Builder (SPR-9480)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册