diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 80a567e5148..11a3cdbee8e 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -599,7 +599,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but // there we want to combine a series of identifiers and dollars into a single identifier private boolean maybeEatMethodOrProperty(boolean nullSafeNavigation) { - if (peekToken(TokenKind.IDENTIFIER)) { + if (peekToken(TokenKind.IDENTIFIER)) { Token methodOrPropertyName = nextToken(); SpelNodeImpl[] args = maybeEatMethodArgs(); if (args==null) { @@ -761,6 +761,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { } return true; } else { + if (desiredTokenKind == TokenKind.IDENTIFIER) { + // might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier + // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum + if (t.kind.ordinal()>=TokenKind.DIV.ordinal() && t.kind.ordinal()<=TokenKind.NOT.ordinal() && t.data!=null) { + // if t.data were null, we'd know it wasn't the textual form, it was the symbol form + return true; + } + } return false; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java index deb311a21dc..c697396175b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java @@ -25,9 +25,10 @@ enum TokenKind { LPAREN("("), RPAREN(")"), COMMA(","), IDENTIFIER, COLON(":"),HASH("#"),RSQUARE("]"), LSQUARE("["), LCURLY("{"),RCURLY("}"), - DOT("."), PLUS("+"), STAR("*"), DIV("/"), NOT("!"), MINUS("-"), SELECT_FIRST("^["), SELECT_LAST("$["), QMARK("?"), PROJECT("!["), - GE(">="),GT(">"),LE("<="),LT("<"),EQ("=="),NE("!="),ASSIGN("="), INSTANCEOF("instanceof"), MATCHES("matches"), BETWEEN("between"), - SELECT("?["), MOD("%"), POWER("^"), + DOT("."), PLUS("+"), STAR("*"), MINUS("-"), SELECT_FIRST("^["), SELECT_LAST("$["), QMARK("?"), PROJECT("!["), + DIV("/"), GE(">="), GT(">"), LE("<="), LT("<"), EQ("=="), NE("!="), + MOD("%"), NOT("!"), ASSIGN("="), INSTANCEOF("instanceof"), MATCHES("matches"), BETWEEN("between"), + SELECT("?["), POWER("^"), ELVIS("?:"), SAFE_NAVI("?."), BEAN_REF("@") ; diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java index a31f16892b6..3811fde747b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java @@ -360,7 +360,7 @@ class Tokenizer { String asString = new String(subarray).toUpperCase(); int idx = Arrays.binarySearch(alternativeOperatorNames,asString); if (idx>=0) { - pushOneCharOrTwoCharToken(TokenKind.valueOf(asString),start); + pushOneCharOrTwoCharToken(TokenKind.valueOf(asString),start,subarray); return; } } @@ -429,8 +429,8 @@ class Tokenizer { pos+=2; } - private void pushOneCharOrTwoCharToken(TokenKind kind, int pos) { - tokens.add(new Token(kind,pos,pos+kind.getLength())); + private void pushOneCharOrTwoCharToken(TokenKind kind, int pos, char[] data) { + tokens.add(new Token(kind,data,pos,pos+kind.getLength())); } // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*; diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java index a2e1dc7d451..94ae9b021fa 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpringEL300Tests.java @@ -1008,4 +1008,56 @@ public class SpringEL300Tests extends ExpressionTestCase { } } + + @Test + public void testReservedWords_8228() throws Exception { + // "DIV","EQ","GE","GT","LE","LT","MOD","NE","NOT" + @SuppressWarnings("unused") + class Reserver { + public Reserver getReserver() { + return this; + } + public String NE = "abc"; + public String ne = "def"; + + public int DIV = 1; + public int div = 3; + + public Map m = new HashMap(); + + @SuppressWarnings("unchecked") + Reserver() { + m.put("NE","xyz"); + } + } + StandardEvaluationContext ctx = new StandardEvaluationContext(new Reserver()); + SpelExpressionParser parser = new SpelExpressionParser(); + String ex = "getReserver().NE"; + SpelExpression exp = null; + exp = parser.parseRaw(ex); + String value = (String)exp.getValue(ctx); + Assert.assertEquals("abc",value); + + ex = "getReserver().ne"; + exp = parser.parseRaw(ex); + value = (String)exp.getValue(ctx); + Assert.assertEquals("def",value); + + ex = "getReserver().m[NE]"; + exp = parser.parseRaw(ex); + value = (String)exp.getValue(ctx); + Assert.assertEquals("xyz",value); + + ex = "getReserver().DIV"; + exp = parser.parseRaw(ex); + Assert.assertEquals(1,exp.getValue(ctx)); + + ex = "getReserver().div"; + exp = parser.parseRaw(ex); + Assert.assertEquals(3,exp.getValue(ctx)); + + exp = parser.parseRaw("NE"); + Assert.assertEquals("abc",exp.getValue(ctx)); + } + }