From e4a926ea3ce69c9d1394c521825ec64fb521b1c2 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Fri, 3 Aug 2012 08:40:45 -0700 Subject: [PATCH] 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 --- .../spel/standard/SpelExpression.java | 17 +- .../expression/spel/standard/Tokenizer.java | 14 +- .../spel/standard/SpelParserTests.java | 339 +++++++++--------- src/dist/changelog.txt | 1 + 4 files changed, 197 insertions(+), 174 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java index 753f8536ad..eac133e671 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java @@ -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; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java index b2e01f90e8..d064a0fb1c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java @@ -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; } } - } diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java index cc9f83883e..098b868e96 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/standard/SpelParserTests.java @@ -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. @@ -16,9 +16,10 @@ package org.springframework.expression.spel.standard; -import junit.framework.Assert; +import static org.junit.Assert.*; import org.junit.Test; + import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.ExpressionException; @@ -36,323 +37,334 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; public class SpelParserTests { @Test - public void theMostBasic() throws EvaluationException,ParseException { + public void theMostBasic() throws EvaluationException, ParseException { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw("2"); - Assert.assertNotNull(expr); - Assert.assertNotNull(expr.getAST()); - Assert.assertEquals(2,expr.getValue()); - Assert.assertEquals(Integer.class,expr.getValueType()); - Assert.assertEquals(2,expr.getAST().getValue(null)); + assertNotNull(expr); + assertNotNull(expr.getAST()); + assertEquals(2, expr.getValue()); + assertEquals(Integer.class, expr.getValueType()); + assertEquals(2, expr.getAST().getValue(null)); } - + @Test public void valueType() throws Exception { SpelExpressionParser parser = new SpelExpressionParser(); EvaluationContext ctx = new StandardEvaluationContext(); - Class c = parser.parseRaw("2").getValueType(); - Assert.assertEquals(Integer.class,c); + Class c = parser.parseRaw("2").getValueType(); + assertEquals(Integer.class, c); c = parser.parseRaw("12").getValueType(ctx); - Assert.assertEquals(Integer.class,c); + assertEquals(Integer.class, c); c = parser.parseRaw("null").getValueType(); - Assert.assertNull(c); + assertNull(c); c = parser.parseRaw("null").getValueType(ctx); - Assert.assertNull(c); - Object o = parser.parseRaw("null").getValue(ctx,Integer.class); - Assert.assertNull(o); + assertNull(c); + Object o = parser.parseRaw("null").getValue(ctx, Integer.class); + assertNull(o); } @Test - public void whitespace() throws EvaluationException,ParseException { + public void whitespace() throws EvaluationException, ParseException { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw("2 + 3"); - Assert.assertEquals(5,expr.getValue()); + assertEquals(5, expr.getValue()); expr = parser.parseRaw("2 + 3"); - Assert.assertEquals(5,expr.getValue()); + assertEquals(5, expr.getValue()); expr = parser.parseRaw("2\n+ 3"); - Assert.assertEquals(5,expr.getValue()); + assertEquals(5, expr.getValue()); expr = parser.parseRaw("2\r\n+\t3"); - Assert.assertEquals(5,expr.getValue()); + assertEquals(5, expr.getValue()); } @Test - public void arithmeticPlus1() throws EvaluationException,ParseException { + public void arithmeticPlus1() throws EvaluationException, ParseException { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw("2+2"); - Assert.assertNotNull(expr); - Assert.assertNotNull(expr.getAST()); - Assert.assertEquals(4,expr.getValue()); + assertNotNull(expr); + assertNotNull(expr.getAST()); + assertEquals(4, expr.getValue()); } @Test - public void arithmeticPlus2() throws EvaluationException,ParseException { + public void arithmeticPlus2() throws EvaluationException, ParseException { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw("37+41"); - Assert.assertEquals(78,expr.getValue()); + assertEquals(78, expr.getValue()); } @Test - public void arithmeticMultiply1() throws EvaluationException,ParseException { + public void arithmeticMultiply1() throws EvaluationException, ParseException { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw("2*3"); - Assert.assertNotNull(expr); - Assert.assertNotNull(expr.getAST()); -// printAst(expr.getAST(),0); - Assert.assertEquals(6,expr.getValue()); + assertNotNull(expr); + assertNotNull(expr.getAST()); + // printAst(expr.getAST(),0); + assertEquals(6, expr.getValue()); } @Test - public void arithmeticPrecedence1() throws EvaluationException,ParseException { + public void arithmeticPrecedence1() throws EvaluationException, ParseException { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw("2*3+5"); - Assert.assertEquals(11,expr.getValue()); + assertEquals(11, expr.getValue()); } @Test public void generalExpressions() throws Exception { + try { SpelExpressionParser parser = new SpelExpressionParser(); parser.parseRaw("new String"); - Assert.fail(); + fail(); } catch (ParseException e) { - Assert.assertTrue(e instanceof SpelParseException); - SpelParseException spe = (SpelParseException)e; - Assert.assertEquals(SpelMessage.MISSING_CONSTRUCTOR_ARGS,spe.getMessageCode()); - Assert.assertEquals(10,spe.getPosition()); + assertTrue(e instanceof SpelParseException); + SpelParseException spe = (SpelParseException) e; + assertEquals(SpelMessage.MISSING_CONSTRUCTOR_ARGS, spe.getMessageCode()); + assertEquals(10, spe.getPosition()); } + try { SpelExpressionParser parser = new SpelExpressionParser(); parser.parseRaw("new String(3,"); - Assert.fail(); + fail(); } catch (ParseException e) { - Assert.assertTrue(e instanceof SpelParseException); - SpelParseException spe = (SpelParseException)e; - Assert.assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS,spe.getMessageCode()); - Assert.assertEquals(10,spe.getPosition()); + assertTrue(e instanceof SpelParseException); + SpelParseException spe = (SpelParseException) e; + assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS, spe.getMessageCode()); + assertEquals(10, spe.getPosition()); } + try { SpelExpressionParser parser = new SpelExpressionParser(); parser.parseRaw("new String(3"); - Assert.fail(); + fail(); } catch (ParseException e) { - Assert.assertTrue(e instanceof SpelParseException); - SpelParseException spe = (SpelParseException)e; - Assert.assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS,spe.getMessageCode()); - Assert.assertEquals(10,spe.getPosition()); + assertTrue(e instanceof SpelParseException); + SpelParseException spe = (SpelParseException) e; + assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS, spe.getMessageCode()); + assertEquals(10, spe.getPosition()); } + try { SpelExpressionParser parser = new SpelExpressionParser(); parser.parseRaw("new String("); - Assert.fail(); + fail(); } catch (ParseException e) { - Assert.assertTrue(e instanceof SpelParseException); - SpelParseException spe = (SpelParseException)e; - Assert.assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS,spe.getMessageCode()); - Assert.assertEquals(10,spe.getPosition()); + assertTrue(e instanceof SpelParseException); + SpelParseException spe = (SpelParseException) e; + assertEquals(SpelMessage.RUN_OUT_OF_ARGUMENTS, spe.getMessageCode()); + assertEquals(10, spe.getPosition()); } + try { SpelExpressionParser parser = new SpelExpressionParser(); parser.parseRaw("\"abc"); - Assert.fail(); + fail(); } catch (ParseException e) { - Assert.assertTrue(e instanceof SpelParseException); - SpelParseException spe = (SpelParseException)e; - Assert.assertEquals(SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING,spe.getMessageCode()); - Assert.assertEquals(0,spe.getPosition()); + assertTrue(e instanceof SpelParseException); + SpelParseException spe = (SpelParseException) e; + assertEquals(SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING, spe.getMessageCode()); + assertEquals(0, spe.getPosition()); } + try { SpelExpressionParser parser = new SpelExpressionParser(); parser.parseRaw("'abc"); - Assert.fail(); + fail(); } catch (ParseException e) { - Assert.assertTrue(e instanceof SpelParseException); - SpelParseException spe = (SpelParseException)e; - Assert.assertEquals(SpelMessage.NON_TERMINATING_QUOTED_STRING,spe.getMessageCode()); - Assert.assertEquals(0,spe.getPosition()); + assertTrue(e instanceof SpelParseException); + SpelParseException spe = (SpelParseException) e; + assertEquals(SpelMessage.NON_TERMINATING_QUOTED_STRING, spe.getMessageCode()); + assertEquals(0, spe.getPosition()); } } - + @Test - public void arithmeticPrecedence2() throws EvaluationException,ParseException { + public void arithmeticPrecedence2() throws EvaluationException, ParseException { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw("2+3*5"); - Assert.assertEquals(17,expr.getValue()); + assertEquals(17, expr.getValue()); } @Test - public void arithmeticPrecedence3() throws EvaluationException,ParseException { + public void arithmeticPrecedence3() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("3+10/2"); - Assert.assertEquals(8,expr.getValue()); + assertEquals(8, expr.getValue()); } @Test - public void arithmeticPrecedence4() throws EvaluationException,ParseException { + public void arithmeticPrecedence4() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("10/2+3"); - Assert.assertEquals(8,expr.getValue()); + assertEquals(8, expr.getValue()); } @Test - public void arithmeticPrecedence5() throws EvaluationException,ParseException { + public void arithmeticPrecedence5() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("(4+10)/2"); - Assert.assertEquals(7,expr.getValue()); + assertEquals(7, expr.getValue()); } @Test - public void arithmeticPrecedence6() throws EvaluationException,ParseException { + public void arithmeticPrecedence6() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("(3+2)*2"); - Assert.assertEquals(10,expr.getValue()); + assertEquals(10, expr.getValue()); } @Test - public void booleanOperators() throws EvaluationException,ParseException { + public void booleanOperators() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("true"); - Assert.assertEquals(Boolean.TRUE,expr.getValue(Boolean.class)); + assertEquals(Boolean.TRUE, expr.getValue(Boolean.class)); expr = new SpelExpressionParser().parseRaw("false"); - Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class)); + assertEquals(Boolean.FALSE, expr.getValue(Boolean.class)); expr = new SpelExpressionParser().parseRaw("false and false"); - Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class)); + assertEquals(Boolean.FALSE, expr.getValue(Boolean.class)); expr = new SpelExpressionParser().parseRaw("true and (true or false)"); - Assert.assertEquals(Boolean.TRUE,expr.getValue(Boolean.class)); + assertEquals(Boolean.TRUE, expr.getValue(Boolean.class)); expr = new SpelExpressionParser().parseRaw("true and true or false"); - Assert.assertEquals(Boolean.TRUE,expr.getValue(Boolean.class)); + assertEquals(Boolean.TRUE, expr.getValue(Boolean.class)); expr = new SpelExpressionParser().parseRaw("!true"); - Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class)); + assertEquals(Boolean.FALSE, expr.getValue(Boolean.class)); expr = new SpelExpressionParser().parseRaw("!(false or true)"); - Assert.assertEquals(Boolean.FALSE,expr.getValue(Boolean.class)); + assertEquals(Boolean.FALSE, expr.getValue(Boolean.class)); } - + @Test - public void testStringLiterals() throws EvaluationException,ParseException { + public void stringLiterals() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'"); - Assert.assertEquals("howdy",expr.getValue()); + assertEquals("howdy", expr.getValue()); expr = new SpelExpressionParser().parseRaw("'hello '' world'"); - Assert.assertEquals("hello ' world",expr.getValue()); + assertEquals("hello ' world", expr.getValue()); } @Test - public void testStringLiterals2() throws EvaluationException,ParseException { + public void stringLiterals2() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("'howdy'.substring(0,2)"); - Assert.assertEquals("ho",expr.getValue()); + assertEquals("ho", expr.getValue()); } - + @Test - public void testPositionalInformation() throws EvaluationException, ParseException { + public void positionalInformation() throws EvaluationException, ParseException { SpelExpression expr = new SpelExpressionParser().parseRaw("true and true or false"); SpelNode rootAst = expr.getAST(); - OpOr operatorOr = (OpOr)rootAst; - OpAnd operatorAnd = (OpAnd)operatorOr.getLeftOperand(); + OpOr operatorOr = (OpOr) rootAst; + OpAnd operatorAnd = (OpAnd) operatorOr.getLeftOperand(); SpelNode rightOrOperand = operatorOr.getRightOperand(); - + // check position for final 'false' - Assert.assertEquals(17, rightOrOperand.getStartPosition()); - Assert.assertEquals(22, rightOrOperand.getEndPosition()); - + assertEquals(17, rightOrOperand.getStartPosition()); + assertEquals(22, rightOrOperand.getEndPosition()); + // check position for first 'true' - Assert.assertEquals(0, operatorAnd.getLeftOperand().getStartPosition()); - Assert.assertEquals(4, operatorAnd.getLeftOperand().getEndPosition()); + assertEquals(0, operatorAnd.getLeftOperand().getStartPosition()); + assertEquals(4, operatorAnd.getLeftOperand().getEndPosition()); // check position for second 'true' - Assert.assertEquals(9, operatorAnd.getRightOperand().getStartPosition()); - Assert.assertEquals(13, operatorAnd.getRightOperand().getEndPosition()); + assertEquals(9, operatorAnd.getRightOperand().getStartPosition()); + assertEquals(13, operatorAnd.getRightOperand().getEndPosition()); // check position for OperatorAnd - Assert.assertEquals(5, operatorAnd.getStartPosition()); - Assert.assertEquals(8, operatorAnd.getEndPosition()); - + assertEquals(5, operatorAnd.getStartPosition()); + assertEquals(8, operatorAnd.getEndPosition()); + // check position for OperatorOr - Assert.assertEquals(14, operatorOr.getStartPosition()); - Assert.assertEquals(16, operatorOr.getEndPosition()); + assertEquals(14, operatorOr.getStartPosition()); + assertEquals(16, operatorOr.getEndPosition()); } - + @Test - public void testTokenKind() { + public void tokenKind() { TokenKind tk = TokenKind.NOT; - Assert.assertFalse(tk.hasPayload()); - Assert.assertEquals("NOT(!)",tk.toString()); + assertFalse(tk.hasPayload()); + assertEquals("NOT(!)", tk.toString()); tk = TokenKind.MINUS; - Assert.assertFalse(tk.hasPayload()); - Assert.assertEquals("MINUS(-)",tk.toString()); - + assertFalse(tk.hasPayload()); + assertEquals("MINUS(-)", tk.toString()); + tk = TokenKind.LITERAL_STRING; - Assert.assertEquals("LITERAL_STRING",tk.toString()); - Assert.assertTrue(tk.hasPayload()); + assertEquals("LITERAL_STRING", tk.toString()); + assertTrue(tk.hasPayload()); } @Test - public void testToken() { - Token token = new Token(TokenKind.NOT,0,3); - Assert.assertEquals(TokenKind.NOT,token.kind); - Assert.assertEquals(0,token.startpos); - Assert.assertEquals(3,token.endpos); - Assert.assertEquals("[NOT(!)](0,3)",token.toString()); - - token = new Token(TokenKind.LITERAL_STRING,"abc".toCharArray(),0,3); - Assert.assertEquals(TokenKind.LITERAL_STRING,token.kind); - Assert.assertEquals(0,token.startpos); - Assert.assertEquals(3,token.endpos); - Assert.assertEquals("[LITERAL_STRING:abc](0,3)",token.toString()); + public void token() { + Token token = new Token(TokenKind.NOT, 0, 3); + assertEquals(TokenKind.NOT, token.kind); + assertEquals(0, token.startpos); + assertEquals(3, token.endpos); + assertEquals("[NOT(!)](0,3)", token.toString()); + + token = new Token(TokenKind.LITERAL_STRING, "abc".toCharArray(), 0, 3); + assertEquals(TokenKind.LITERAL_STRING, token.kind); + assertEquals(0, token.startpos); + assertEquals(3, token.endpos); + assertEquals("[LITERAL_STRING:abc](0,3)", token.toString()); } @Test - public void testExceptions() { + public void exceptions() { ExpressionException exprEx = new ExpressionException("test"); - Assert.assertEquals("test", exprEx.getMessage()); - Assert.assertEquals("test", exprEx.toDetailedString()); + assertEquals("test", exprEx.getMessage()); + assertEquals("test", exprEx.toDetailedString()); - exprEx = new ExpressionException("wibble","test"); - Assert.assertEquals("test", exprEx.getMessage()); - Assert.assertEquals("Expression 'wibble': test", exprEx.toDetailedString()); + exprEx = new ExpressionException("wibble", "test"); + assertEquals("test", exprEx.getMessage()); + assertEquals("Expression 'wibble': test", exprEx.toDetailedString()); - exprEx = new ExpressionException("wibble",3, "test"); - Assert.assertEquals("test", exprEx.getMessage()); - Assert.assertEquals("Expression 'wibble' @ 3: test", exprEx.toDetailedString()); + exprEx = new ExpressionException("wibble", 3, "test"); + assertEquals("test", exprEx.getMessage()); + assertEquals("Expression 'wibble' @ 3: test", exprEx.toDetailedString()); } @Test - public void testNumerics() { - checkNumber("2",2,Integer.class); - checkNumber("22",22,Integer.class); - checkNumber("+22",22,Integer.class); - checkNumber("-22",-22,Integer.class); + public void parseMethodsOnNumbers() { + checkNumber("3.14.toString()", "3.14", String.class); + checkNumber("3.toString()", "3", String.class); + } - checkNumber("2L",2L,Long.class); - checkNumber("22l",22L,Long.class); - - checkNumber("0x1",1,Integer.class); - checkNumber("0x1L",1L,Long.class); - checkNumber("0xa",10,Integer.class); - checkNumber("0xAL",10L,Long.class); + @Test + public void numerics() { + checkNumber("2", 2, Integer.class); + checkNumber("22", 22, Integer.class); + checkNumber("+22", 22, Integer.class); + checkNumber("-22", -22, Integer.class); - checkNumberError("0x",SpelMessage.NOT_AN_INTEGER); - checkNumberError("0xL",SpelMessage.NOT_A_LONG); + checkNumber("2L", 2L, Long.class); + checkNumber("22l", 22L, Long.class); - checkNumberError(".324",SpelMessage.UNEXPECTED_DATA_AFTER_DOT); + checkNumber("0x1", 1, Integer.class); + checkNumber("0x1L", 1L, Long.class); + checkNumber("0xa", 10, Integer.class); + checkNumber("0xAL", 10L, Long.class); - checkNumberError("3.4L",SpelMessage.REAL_CANNOT_BE_LONG); + checkNumberError("0x", SpelMessage.NOT_AN_INTEGER); + checkNumberError("0xL", SpelMessage.NOT_A_LONG); + + checkNumberError(".324", SpelMessage.UNEXPECTED_DATA_AFTER_DOT); + + checkNumberError("3.4L", SpelMessage.REAL_CANNOT_BE_LONG); // Number is parsed as a float, but immediately promoted to a double - checkNumber("3.5f",3.5d,Double.class); - + checkNumber("3.5f", 3.5d, Double.class); + checkNumber("1.2e3", 1.2e3d, Double.class); checkNumber("1.2e+3", 1.2e3d, Double.class); checkNumber("1.2e-3", 1.2e-3d, Double.class); checkNumber("1.2e3", 1.2e3d, Double.class); - checkNumber("1.e+3", 1.e3d, Double.class); checkNumber("1e+3", 1e3d, Double.class); } - + private void checkNumber(String expression, Object value, Class type) { try { SpelExpressionParser parser = new SpelExpressionParser(); SpelExpression expr = parser.parseRaw(expression); Object o = expr.getValue(); - Assert.assertEquals(value,o); - Assert.assertEquals(type,o.getClass()); + assertEquals(value, o); + assertEquals(type, o.getClass()); } catch (Exception e) { e.printStackTrace(); - Assert.fail(e.getMessage()); + fail(e.getMessage()); } } @@ -360,11 +372,12 @@ public class SpelParserTests { try { SpelExpressionParser parser = new SpelExpressionParser(); parser.parseRaw(expression); - Assert.fail(); + fail(); } catch (ParseException e) { - Assert.assertTrue(e instanceof SpelParseException); - SpelParseException spe = (SpelParseException)e; - Assert.assertEquals(expectedMessage,spe.getMessageCode()); + assertTrue(e instanceof SpelParseException); + SpelParseException spe = (SpelParseException) e; + assertEquals(expectedMessage, spe.getMessageCode()); } } + } diff --git a/src/dist/changelog.txt b/src/dist/changelog.txt index d42c161f37..e5a777baa1 100644 --- a/src/dist/changelog.txt +++ b/src/dist/changelog.txt @@ -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)