1 /* 2 * Copyright 2002-2009 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.expression.spel.standard; 18 19 import java.util.ArrayList; 20 import java.util.List; 21 import java.util.Stack; 22 23 import org.springframework.expression.ParseException; 24 import org.springframework.expression.ParserContext; 25 import org.springframework.expression.common.TemplateAwareExpressionParser; 26 import org.springframework.expression.spel.InternalParseException; 27 import org.springframework.expression.spel.SpelMessage; 28 import org.springframework.expression.spel.SpelParseException; 29 import org.springframework.expression.spel.SpelParserConfiguration; 30 import org.springframework.expression.spel.ast.Assign; 31 import org.springframework.expression.spel.ast.BeanReference; 32 import org.springframework.expression.spel.ast.BooleanLiteral; 33 import org.springframework.expression.spel.ast.CompoundExpression; 34 import org.springframework.expression.spel.ast.ConstructorReference; 35 import org.springframework.expression.spel.ast.Elvis; 36 import org.springframework.expression.spel.ast.FunctionReference; 37 import org.springframework.expression.spel.ast.Identifier; 38 import org.springframework.expression.spel.ast.Indexer; 39 import org.springframework.expression.spel.ast.InlineList; 40 import org.springframework.expression.spel.ast.Literal; 41 import org.springframework.expression.spel.ast.MethodReference; 42 import org.springframework.expression.spel.ast.NullLiteral; 43 import org.springframework.expression.spel.ast.OpAnd; 44 import org.springframework.expression.spel.ast.OpDivide; 45 import org.springframework.expression.spel.ast.OpEQ; 46 import org.springframework.expression.spel.ast.OpGE; 47 import org.springframework.expression.spel.ast.OpGT; 48 import org.springframework.expression.spel.ast.OpLE; 49 import org.springframework.expression.spel.ast.OpLT; 50 import org.springframework.expression.spel.ast.OpMinus; 51 import org.springframework.expression.spel.ast.OpModulus; 52 import org.springframework.expression.spel.ast.OpMultiply; 53 import org.springframework.expression.spel.ast.OpNE; 54 import org.springframework.expression.spel.ast.OpOr; 55 import org.springframework.expression.spel.ast.OpPlus; 56 import org.springframework.expression.spel.ast.OperatorInstanceof; 57 import org.springframework.expression.spel.ast.OperatorMatches; 58 import org.springframework.expression.spel.ast.OperatorNot; 59 import org.springframework.expression.spel.ast.OperatorPower; 60 import org.springframework.expression.spel.ast.Projection; 61 import org.springframework.expression.spel.ast.PropertyOrFieldReference; 62 import org.springframework.expression.spel.ast.QualifiedIdentifier; 63 import org.springframework.expression.spel.ast.Selection; 64 import org.springframework.expression.spel.ast.SpelNodeImpl; 65 import org.springframework.expression.spel.ast.StringLiteral; 66 import org.springframework.expression.spel.ast.Ternary; 67 import org.springframework.expression.spel.ast.TypeReference; 68 import org.springframework.expression.spel.ast.VariableReference; 69 import org.springframework.util.Assert; 70 71 /** 72 * Hand written SpEL parser. Instances are reusable but are not thread safe. 73 * 74 * @author Andy Clement 75 * @since 3.0 76 */ 77 class InternalSpelExpressionParser extends TemplateAwareExpressionParser { 78 79 // The expression being parsed 80 private String expressionString; 81 82 // The token stream constructed from that expression string 83 private List<Token> tokenStream; 84 85 // length of a populated token stream 86 private int tokenStreamLength; 87 88 // Current location in the token stream when processing tokens 89 private int tokenStreamPointer; 90 91 // For rules that build nodes, they are stacked here for return 92 private Stack<SpelNodeImpl> constructedNodes = new Stack<SpelNodeImpl>(); 93 94 private SpelParserConfiguration configuration; 95 96 97 /** 98 * Create a parser with some configured behavior. 99 * @param configuration custom configuration options 100 */ InternalSpelExpressionParser(SpelParserConfiguration configuration)101 public InternalSpelExpressionParser(SpelParserConfiguration configuration) { 102 this.configuration = configuration; 103 } 104 105 106 @Override doParseExpression(String expressionString, ParserContext context)107 protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { 108 try { 109 this.expressionString = expressionString; 110 Tokenizer tokenizer = new Tokenizer(expressionString); 111 tokenizer.process(); 112 tokenStream = tokenizer.getTokens(); 113 tokenStreamLength = tokenStream.size(); 114 tokenStreamPointer = 0; 115 constructedNodes.clear(); 116 SpelNodeImpl ast = eatExpression(); 117 if (moreTokens()) { 118 throw new SpelParseException(peekToken().startpos,SpelMessage.MORE_INPUT,toString(nextToken())); 119 } 120 Assert.isTrue(constructedNodes.isEmpty()); 121 return new SpelExpression(expressionString, ast, configuration); 122 } 123 catch (InternalParseException ipe) { 124 throw ipe.getCause(); 125 } 126 } 127 128 // expression 129 // : logicalOrExpression 130 // ( (ASSIGN^ logicalOrExpression) 131 // | (DEFAULT^ logicalOrExpression) 132 // | (QMARK^ expression COLON! expression) 133 // | (ELVIS^ expression))?; eatExpression()134 private SpelNodeImpl eatExpression() { 135 SpelNodeImpl expr = eatLogicalOrExpression(); 136 if (moreTokens()) { 137 Token t = peekToken(); 138 if (t.kind==TokenKind.ASSIGN) { // a=b 139 if (expr==null) { 140 expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1)); 141 } 142 nextToken(); 143 SpelNodeImpl assignedValue = eatLogicalOrExpression(); 144 return new Assign(toPos(t),expr,assignedValue); 145 } else if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) 146 if (expr==null) { 147 expr = new NullLiteral(toPos(t.startpos-1,t.endpos-2)); 148 } 149 nextToken(); // elvis has left the building 150 SpelNodeImpl valueIfNull = eatExpression(); 151 if (valueIfNull==null) { 152 valueIfNull = new NullLiteral(toPos(t.startpos+1,t.endpos+1)); 153 } 154 return new Elvis(toPos(t),expr,valueIfNull); 155 } else if (t.kind==TokenKind.QMARK) { // a?b:c 156 if (expr==null) { 157 expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1)); 158 } 159 nextToken(); 160 SpelNodeImpl ifTrueExprValue = eatExpression(); 161 eatToken(TokenKind.COLON); 162 SpelNodeImpl ifFalseExprValue = eatExpression(); 163 return new Ternary(toPos(t),expr,ifTrueExprValue,ifFalseExprValue); 164 } 165 } 166 return expr; 167 } 168 169 //logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*; eatLogicalOrExpression()170 private SpelNodeImpl eatLogicalOrExpression() { 171 SpelNodeImpl expr = eatLogicalAndExpression(); 172 while (peekIdentifierToken("or")) { 173 Token t = nextToken(); //consume OR 174 SpelNodeImpl rhExpr = eatLogicalAndExpression(); 175 checkRightOperand(t,rhExpr); 176 expr = new OpOr(toPos(t),expr,rhExpr); 177 } 178 return expr; 179 } 180 181 // logicalAndExpression : relationalExpression (AND^ relationalExpression)*; eatLogicalAndExpression()182 private SpelNodeImpl eatLogicalAndExpression() { 183 SpelNodeImpl expr = eatRelationalExpression(); 184 while (peekIdentifierToken("and")) { 185 Token t = nextToken();// consume 'AND' 186 SpelNodeImpl rhExpr = eatRelationalExpression(); 187 checkRightOperand(t,rhExpr); 188 expr = new OpAnd(toPos(t),expr,rhExpr); 189 } 190 return expr; 191 } 192 193 // relationalExpression : sumExpression (relationalOperator^ sumExpression)?; eatRelationalExpression()194 private SpelNodeImpl eatRelationalExpression() { 195 SpelNodeImpl expr = eatSumExpression(); 196 Token relationalOperatorToken = maybeEatRelationalOperator(); 197 if (relationalOperatorToken != null) { 198 Token t = nextToken(); //consume relational operator token 199 SpelNodeImpl rhExpr = eatSumExpression(); 200 checkRightOperand(t,rhExpr); 201 TokenKind tk = relationalOperatorToken.kind; 202 if (relationalOperatorToken.isNumericRelationalOperator()) { 203 int pos = toPos(t); 204 if (tk==TokenKind.GT) { 205 return new OpGT(pos,expr,rhExpr); 206 } else if (tk==TokenKind.LT) { 207 return new OpLT(pos,expr,rhExpr); 208 } else if (tk==TokenKind.LE) { 209 return new OpLE(pos,expr,rhExpr); 210 } else if (tk==TokenKind.GE) { 211 return new OpGE(pos,expr,rhExpr); 212 } else if (tk == TokenKind.EQ) { 213 return new OpEQ(pos,expr,rhExpr); 214 } else { 215 Assert.isTrue(tk == TokenKind.NE); 216 return new OpNE(pos,expr,rhExpr); 217 } 218 } 219 if (tk==TokenKind.INSTANCEOF) { 220 return new OperatorInstanceof(toPos(t),expr,rhExpr); 221 } else if (tk==TokenKind.MATCHES) { 222 return new OperatorMatches(toPos(t),expr,rhExpr); 223 } else { 224 Assert.isTrue(tk==TokenKind.BETWEEN); 225 return new org.springframework.expression.spel.ast.OperatorBetween(toPos(t),expr,rhExpr); 226 } 227 } 228 return expr; 229 } 230 231 //sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*; eatSumExpression()232 private SpelNodeImpl eatSumExpression() { 233 SpelNodeImpl expr = eatProductExpression(); 234 while (peekToken(TokenKind.PLUS,TokenKind.MINUS)) { 235 Token t = nextToken();//consume PLUS or MINUS 236 SpelNodeImpl rhExpr = eatProductExpression(); 237 checkRightOperand(t,rhExpr); 238 if (t.kind==TokenKind.PLUS) { 239 expr = new OpPlus(toPos(t),expr,rhExpr); 240 } else { 241 Assert.isTrue(t.kind==TokenKind.MINUS); 242 expr = new OpMinus(toPos(t),expr,rhExpr); 243 } 244 } 245 return expr; 246 } 247 248 // productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ; eatProductExpression()249 private SpelNodeImpl eatProductExpression() { 250 SpelNodeImpl expr = eatPowerExpression(); 251 while (peekToken(TokenKind.STAR,TokenKind.DIV,TokenKind.MOD)) { 252 Token t = nextToken(); // consume STAR/DIV/MOD 253 SpelNodeImpl rhExpr = eatPowerExpression(); 254 checkRightOperand(t,rhExpr); 255 if (t.kind==TokenKind.STAR) { 256 expr = new OpMultiply(toPos(t),expr,rhExpr); 257 } else if (t.kind==TokenKind.DIV) { 258 expr = new OpDivide(toPos(t),expr,rhExpr); 259 } else { 260 Assert.isTrue(t.kind==TokenKind.MOD); 261 expr = new OpModulus(toPos(t),expr,rhExpr); 262 } 263 } 264 return expr; 265 } 266 267 // powerExpr : unaryExpression (POWER^ unaryExpression)? ; eatPowerExpression()268 private SpelNodeImpl eatPowerExpression() { 269 SpelNodeImpl expr = eatUnaryExpression(); 270 if (peekToken(TokenKind.POWER)) { 271 Token t = nextToken();//consume POWER 272 SpelNodeImpl rhExpr = eatUnaryExpression(); 273 checkRightOperand(t,rhExpr); 274 return new OperatorPower(toPos(t),expr, rhExpr); 275 } 276 return expr; 277 } 278 279 // unaryExpression: (PLUS^ | MINUS^ | BANG^) unaryExpression | primaryExpression ; eatUnaryExpression()280 private SpelNodeImpl eatUnaryExpression() { 281 if (peekToken(TokenKind.PLUS,TokenKind.MINUS,TokenKind.NOT)) { 282 Token t = nextToken(); 283 SpelNodeImpl expr = eatUnaryExpression(); 284 if (t.kind==TokenKind.NOT) { 285 return new OperatorNot(toPos(t),expr); 286 } else if (t.kind==TokenKind.PLUS) { 287 return new OpPlus(toPos(t),expr); 288 } else { 289 Assert.isTrue(t.kind==TokenKind.MINUS); 290 return new OpMinus(toPos(t),expr); 291 } 292 } else { 293 return eatPrimaryExpression(); 294 } 295 } 296 297 // primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?); eatPrimaryExpression()298 private SpelNodeImpl eatPrimaryExpression() { 299 List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>(); 300 SpelNodeImpl start = eatStartNode(); // always a start node 301 nodes.add(start); 302 while (maybeEatNode()) { 303 nodes.add(pop()); 304 } 305 if (nodes.size()==1) { 306 return nodes.get(0); 307 } else { 308 return new CompoundExpression(toPos(start.getStartPosition(),nodes.get(nodes.size()-1).getEndPosition()),nodes.toArray(new SpelNodeImpl[nodes.size()])); 309 } 310 } 311 312 // node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+; maybeEatNode()313 private boolean maybeEatNode() { 314 SpelNodeImpl expr = null; 315 if (peekToken(TokenKind.DOT,TokenKind.SAFE_NAVI)) { 316 expr = eatDottedNode(); 317 } else { 318 expr = maybeEatNonDottedNode(); 319 } 320 if (expr==null) { 321 return false; 322 } else { 323 push(expr); 324 return true; 325 } 326 } 327 328 // nonDottedNode: indexer; maybeEatNonDottedNode()329 private SpelNodeImpl maybeEatNonDottedNode() { 330 if (peekToken(TokenKind.LSQUARE)) { 331 if (maybeEatIndexer()) { 332 return pop(); 333 } 334 } 335 return null; 336 } 337 338 //dottedNode 339 // : ((methodOrProperty 340 // | functionOrVar 341 // | projection 342 // | selection 343 // | firstSelection 344 // | lastSelection 345 // )) 346 // ; eatDottedNode()347 private SpelNodeImpl eatDottedNode() { 348 Token t = nextToken();// it was a '.' or a '?.' 349 boolean nullSafeNavigation = t.kind==TokenKind.SAFE_NAVI; 350 if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() || maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) { 351 return pop(); 352 } 353 if (peekToken()==null) { 354 // unexpectedly ran out of data 355 raiseInternalException(t.startpos,SpelMessage.OOD); 356 } else { 357 raiseInternalException(t.startpos,SpelMessage.UNEXPECTED_DATA_AFTER_DOT,toString(peekToken())); 358 } 359 return null; 360 } 361 362 // functionOrVar 363 // : (POUND ID LPAREN) => function 364 // | var 365 // 366 // function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs); 367 // var : POUND id=ID -> ^(VARIABLEREF[$id]); maybeEatFunctionOrVar()368 private boolean maybeEatFunctionOrVar() { 369 if (!peekToken(TokenKind.HASH)) { 370 return false; 371 } 372 Token t = nextToken(); 373 Token functionOrVariableName = eatToken(TokenKind.IDENTIFIER); 374 SpelNodeImpl[] args = maybeEatMethodArgs(); 375 if (args==null) { 376 push(new VariableReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos))); 377 return true; 378 } else { 379 push(new FunctionReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos),args)); 380 return true; 381 } 382 } 383 384 // methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!; maybeEatMethodArgs()385 private SpelNodeImpl[] maybeEatMethodArgs() { 386 if (!peekToken(TokenKind.LPAREN)) { 387 return null; 388 } 389 List<SpelNodeImpl> args = new ArrayList<SpelNodeImpl>(); 390 consumeArguments(args); 391 eatToken(TokenKind.RPAREN); 392 return args.toArray(new SpelNodeImpl[args.size()]); 393 } 394 eatConstructorArgs(List<SpelNodeImpl> accumulatedArguments)395 private void eatConstructorArgs(List<SpelNodeImpl> accumulatedArguments) { 396 if (!peekToken(TokenKind.LPAREN)) { 397 throw new InternalParseException(new SpelParseException(expressionString,positionOf(peekToken()),SpelMessage.MISSING_CONSTRUCTOR_ARGS)); 398 } 399 consumeArguments(accumulatedArguments); 400 eatToken(TokenKind.RPAREN); 401 } 402 403 /** 404 * Used for consuming arguments for either a method or a constructor call 405 */ consumeArguments(List<SpelNodeImpl> accumulatedArguments)406 private void consumeArguments(List<SpelNodeImpl> accumulatedArguments) { 407 int pos = peekToken().startpos; 408 Token next = null; 409 do { 410 nextToken();// consume ( (first time through) or comma (subsequent times) 411 Token t = peekToken(); 412 if (t==null) { 413 raiseInternalException(pos,SpelMessage.RUN_OUT_OF_ARGUMENTS); 414 } 415 if (t.kind!=TokenKind.RPAREN) { 416 accumulatedArguments.add(eatExpression()); 417 } 418 next = peekToken(); 419 } while (next!=null && next.kind==TokenKind.COMMA); 420 if (next==null) { 421 raiseInternalException(pos,SpelMessage.RUN_OUT_OF_ARGUMENTS); 422 } 423 } 424 positionOf(Token t)425 private int positionOf(Token t) { 426 if (t==null) { 427 // if null assume the problem is because the right token was 428 // not found at the end of the expression 429 return expressionString.length(); 430 } else { 431 return t.startpos; 432 } 433 } 434 435 436 //startNode 437 // : parenExpr | literal 438 // | type 439 // | methodOrProperty 440 // | functionOrVar 441 // | projection 442 // | selection 443 // | firstSelection 444 // | lastSelection 445 // | indexer 446 // | constructor eatStartNode()447 private SpelNodeImpl eatStartNode() { 448 if (maybeEatLiteral()) { 449 return pop(); 450 } else if (maybeEatParenExpression()) { 451 return pop(); 452 } else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { 453 return pop(); 454 } else if (maybeEatBeanReference()) { 455 return pop(); 456 } else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { 457 return pop(); 458 } else if (maybeEatInlineList()) { 459 return pop(); 460 } else { 461 return null; 462 } 463 } 464 465 // parse: @beanname @'bean.name' 466 // quoted if dotted maybeEatBeanReference()467 private boolean maybeEatBeanReference() { 468 if (peekToken(TokenKind.BEAN_REF)) { 469 Token beanRefToken = nextToken(); 470 Token beanNameToken = null; 471 String beanname = null; 472 if (peekToken(TokenKind.IDENTIFIER)) { 473 beanNameToken = eatToken(TokenKind.IDENTIFIER); 474 beanname = beanNameToken.data; 475 } else if (peekToken(TokenKind.LITERAL_STRING)) { 476 beanNameToken = eatToken(TokenKind.LITERAL_STRING); 477 beanname = beanNameToken.stringValue(); 478 beanname = beanname.substring(1, beanname.length() - 1); 479 } else { 480 raiseInternalException(beanRefToken.startpos,SpelMessage.INVALID_BEAN_REFERENCE); 481 } 482 483 BeanReference beanReference = new BeanReference(toPos(beanNameToken),beanname); 484 constructedNodes.push(beanReference); 485 return true; 486 } 487 return false; 488 } 489 maybeEatTypeReference()490 private boolean maybeEatTypeReference() { 491 if (peekToken(TokenKind.IDENTIFIER)) { 492 Token typeName = peekToken(); 493 if (!typeName.stringValue().equals("T")) { 494 return false; 495 } 496 nextToken(); 497 eatToken(TokenKind.LPAREN); 498 SpelNodeImpl node = eatPossiblyQualifiedId(); 499 // dotted qualified id 500 eatToken(TokenKind.RPAREN); 501 constructedNodes.push(new TypeReference(toPos(typeName),node)); 502 return true; 503 } 504 return false; 505 } 506 maybeEatNullReference()507 private boolean maybeEatNullReference() { 508 if (peekToken(TokenKind.IDENTIFIER)) { 509 Token nullToken = peekToken(); 510 if (!nullToken.stringValue().equals("null")) { 511 return false; 512 } 513 nextToken(); 514 constructedNodes.push(new NullLiteral(toPos(nullToken))); 515 return true; 516 } 517 return false; 518 } 519 520 //projection: PROJECT^ expression RCURLY!; maybeEatProjection(boolean nullSafeNavigation)521 private boolean maybeEatProjection(boolean nullSafeNavigation) { 522 Token t = peekToken(); 523 if (!peekToken(TokenKind.PROJECT,true)) { 524 return false; 525 } 526 SpelNodeImpl expr = eatExpression(); 527 eatToken(TokenKind.RSQUARE); 528 constructedNodes.push(new Projection(nullSafeNavigation, toPos(t), expr)); 529 return true; 530 } 531 532 // list = LCURLY (element (COMMA element)*) RCURLY maybeEatInlineList()533 private boolean maybeEatInlineList() { 534 Token t = peekToken(); 535 if (!peekToken(TokenKind.LCURLY,true)) { 536 return false; 537 } 538 SpelNodeImpl expr = null; 539 Token closingCurly = peekToken(); 540 if (peekToken(TokenKind.RCURLY,true)) { 541 // empty list '[]' 542 expr = new InlineList(toPos(t.startpos,closingCurly.endpos)); 543 } else { 544 List<SpelNodeImpl> listElements = new ArrayList<SpelNodeImpl>(); 545 do { 546 listElements.add(eatExpression()); 547 } while (peekToken(TokenKind.COMMA,true)); 548 closingCurly = eatToken(TokenKind.RCURLY); 549 expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()])); 550 } 551 constructedNodes.push(expr); 552 return true; 553 } 554 maybeEatIndexer()555 private boolean maybeEatIndexer() { 556 Token t = peekToken(); 557 if (!peekToken(TokenKind.LSQUARE,true)) { 558 return false; 559 } 560 SpelNodeImpl expr = eatExpression(); 561 eatToken(TokenKind.RSQUARE); 562 constructedNodes.push(new Indexer(toPos(t),expr)); 563 return true; 564 } 565 maybeEatSelection(boolean nullSafeNavigation)566 private boolean maybeEatSelection(boolean nullSafeNavigation) { 567 Token t = peekToken(); 568 if (!peekSelectToken()) { 569 return false; 570 } 571 nextToken(); 572 SpelNodeImpl expr = eatExpression(); 573 eatToken(TokenKind.RSQUARE); 574 if (t.kind==TokenKind.SELECT_FIRST) { 575 constructedNodes.push(new Selection(nullSafeNavigation,Selection.FIRST,toPos(t),expr)); 576 } else if (t.kind==TokenKind.SELECT_LAST) { 577 constructedNodes.push(new Selection(nullSafeNavigation,Selection.LAST,toPos(t),expr)); 578 } else { 579 constructedNodes.push(new Selection(nullSafeNavigation,Selection.ALL,toPos(t),expr)); 580 } 581 return true; 582 } 583 584 /** 585 * Eat an identifier, possibly qualified (meaning that it is dotted). 586 * TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c) 587 */ eatPossiblyQualifiedId()588 private SpelNodeImpl eatPossiblyQualifiedId() { 589 List<SpelNodeImpl> qualifiedIdPieces = new ArrayList<SpelNodeImpl>(); 590 Token startnode = eatToken(TokenKind.IDENTIFIER); 591 qualifiedIdPieces.add(new Identifier(startnode.stringValue(),toPos(startnode))); 592 while (peekToken(TokenKind.DOT,true)) { 593 Token node = eatToken(TokenKind.IDENTIFIER); 594 qualifiedIdPieces.add(new Identifier(node.stringValue(),toPos(node))); 595 } 596 return new QualifiedIdentifier(toPos(startnode.startpos,qualifiedIdPieces.get(qualifiedIdPieces.size()-1).getEndPosition()),qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()])); 597 } 598 599 // This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but 600 // there we want to combine a series of identifiers and dollars into a single identifier maybeEatMethodOrProperty(boolean nullSafeNavigation)601 private boolean maybeEatMethodOrProperty(boolean nullSafeNavigation) { 602 if (peekToken(TokenKind.IDENTIFIER)) { 603 Token methodOrPropertyName = nextToken(); 604 SpelNodeImpl[] args = maybeEatMethodArgs(); 605 if (args==null) { 606 // property 607 push(new PropertyOrFieldReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName))); 608 return true; 609 } else { 610 // methodreference 611 push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args)); 612 // TODO what is the end position for a method reference? the name or the last arg? 613 return true; 614 } 615 } 616 return false; 617 618 } 619 620 //constructor 621 //: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs) maybeEatConstructorReference()622 private boolean maybeEatConstructorReference() { 623 if (peekIdentifierToken("new")) { 624 Token newToken = nextToken(); 625 SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); 626 List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>(); 627 nodes.add(possiblyQualifiedConstructorName); 628 if (peekToken(TokenKind.LSQUARE)) { 629 // array initializer 630 List<SpelNodeImpl> dimensions = new ArrayList<SpelNodeImpl>(); 631 while (peekToken(TokenKind.LSQUARE,true)) { 632 if (!peekToken(TokenKind.RSQUARE)) { 633 dimensions.add(eatExpression()); 634 } else { 635 dimensions.add(null); 636 } 637 eatToken(TokenKind.RSQUARE); 638 } 639 if (maybeEatInlineList()) { 640 nodes.add(pop()); 641 } 642 push(new ConstructorReference(toPos(newToken), dimensions.toArray(new SpelNodeImpl[dimensions.size()]), 643 nodes.toArray(new SpelNodeImpl[nodes.size()]))); 644 } else { 645 // regular constructor invocation 646 eatConstructorArgs(nodes); 647 // TODO correct end position? 648 push(new ConstructorReference(toPos(newToken), nodes.toArray(new SpelNodeImpl[nodes.size()]))); 649 } 650 return true; 651 } 652 return false; 653 } 654 push(SpelNodeImpl newNode)655 private void push(SpelNodeImpl newNode) { 656 constructedNodes.push(newNode); 657 } 658 pop()659 private SpelNodeImpl pop() { 660 return constructedNodes.pop(); 661 } 662 663 // literal 664 // : INTEGER_LITERAL 665 // | boolLiteral 666 // | STRING_LITERAL 667 // | HEXADECIMAL_INTEGER_LITERAL 668 // | REAL_LITERAL 669 // | DQ_STRING_LITERAL 670 // | NULL_LITERAL maybeEatLiteral()671 private boolean maybeEatLiteral() { 672 Token t = peekToken(); 673 if (t==null) { 674 return false; 675 } 676 if (t.kind==TokenKind.LITERAL_INT) { 677 push(Literal.getIntLiteral(t.data, toPos(t), 10)); 678 } else if (t.kind==TokenKind.LITERAL_LONG) { 679 push(Literal.getLongLiteral(t.data, toPos(t), 10)); 680 } else if (t.kind==TokenKind.LITERAL_HEXINT) { 681 push(Literal.getIntLiteral(t.data, toPos(t), 16)); 682 } else if (t.kind==TokenKind.LITERAL_HEXLONG) { 683 push(Literal.getLongLiteral(t.data, toPos(t), 16)); 684 } else if (t.kind==TokenKind.LITERAL_REAL) { 685 push(Literal.getRealLiteral(t.data, toPos(t),false)); 686 } else if (t.kind==TokenKind.LITERAL_REAL_FLOAT) { 687 push(Literal.getRealLiteral(t.data, toPos(t), true)); 688 } else if (peekIdentifierToken("true")) { 689 push(new BooleanLiteral(t.data,toPos(t),true)); 690 } else if (peekIdentifierToken("false")) { 691 push(new BooleanLiteral(t.data,toPos(t),false)); 692 } else if (t.kind==TokenKind.LITERAL_STRING) { 693 push(new StringLiteral(t.data,toPos(t),t.data)); 694 } else { 695 return false; 696 } 697 nextToken(); 698 return true; 699 } 700 701 //parenExpr : LPAREN! expression RPAREN!; maybeEatParenExpression()702 private boolean maybeEatParenExpression() { 703 if (peekToken(TokenKind.LPAREN)) { 704 nextToken(); 705 SpelNodeImpl expr = eatExpression(); 706 eatToken(TokenKind.RPAREN); 707 push(expr); 708 return true; 709 } else { 710 return false; 711 } 712 } 713 714 // relationalOperator 715 // : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN 716 // | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES maybeEatRelationalOperator()717 private Token maybeEatRelationalOperator() { 718 Token t = peekToken(); 719 if (t==null) { 720 return null; 721 } 722 if (t.isNumericRelationalOperator()) { 723 return t; 724 } 725 if (t.isIdentifier()) { 726 String idString = t.stringValue(); 727 if (idString.equalsIgnoreCase("instanceof")) { 728 return t.asInstanceOfToken(); 729 } else if (idString.equalsIgnoreCase("matches")) { 730 return t.asMatchesToken(); 731 } else if (idString.equalsIgnoreCase("between")) { 732 return t.asBetweenToken(); 733 } 734 } 735 return null; 736 } 737 eatToken(TokenKind expectedKind)738 private Token eatToken(TokenKind expectedKind) { 739 Token t = nextToken(); 740 if (t==null) { 741 raiseInternalException( expressionString.length(), SpelMessage.OOD); 742 } 743 if (t.kind!=expectedKind) { 744 raiseInternalException(t.startpos,SpelMessage.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),t.getKind().toString().toLowerCase()); 745 } 746 return t; 747 } 748 peekToken(TokenKind desiredTokenKind)749 private boolean peekToken(TokenKind desiredTokenKind) { 750 return peekToken(desiredTokenKind,false); 751 } 752 peekToken(TokenKind desiredTokenKind, boolean consumeIfMatched)753 private boolean peekToken(TokenKind desiredTokenKind, boolean consumeIfMatched) { 754 if (!moreTokens()) { 755 return false; 756 } 757 Token t = peekToken(); 758 if (t.kind==desiredTokenKind) { 759 if (consumeIfMatched) { 760 tokenStreamPointer++; 761 } 762 return true; 763 } else { 764 if (desiredTokenKind == TokenKind.IDENTIFIER) { 765 // might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier 766 // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum 767 if (t.kind.ordinal()>=TokenKind.DIV.ordinal() && t.kind.ordinal()<=TokenKind.NOT.ordinal() && t.data!=null) { 768 // if t.data were null, we'd know it wasn't the textual form, it was the symbol form 769 return true; 770 } 771 } 772 return false; 773 } 774 } 775 peekToken(TokenKind possible1,TokenKind possible2)776 private boolean peekToken(TokenKind possible1,TokenKind possible2) { 777 if (!moreTokens()) return false; 778 Token t = peekToken(); 779 return t.kind==possible1 || t.kind==possible2; 780 } 781 peekToken(TokenKind possible1,TokenKind possible2, TokenKind possible3)782 private boolean peekToken(TokenKind possible1,TokenKind possible2, TokenKind possible3) { 783 if (!moreTokens()) return false; 784 Token t = peekToken(); 785 return t.kind==possible1 || t.kind==possible2 || t.kind==possible3; 786 } 787 peekIdentifierToken(String identifierString)788 private boolean peekIdentifierToken(String identifierString) { 789 if (!moreTokens()) { 790 return false; 791 } 792 Token t = peekToken(); 793 return t.kind==TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString); 794 } 795 peekSelectToken()796 private boolean peekSelectToken() { 797 if (!moreTokens()) return false; 798 Token t = peekToken(); 799 return t.kind==TokenKind.SELECT || t.kind==TokenKind.SELECT_FIRST || t.kind==TokenKind.SELECT_LAST; 800 } 801 802 moreTokens()803 private boolean moreTokens() { 804 return tokenStreamPointer<tokenStream.size(); 805 } 806 nextToken()807 private Token nextToken() { 808 if (tokenStreamPointer>=tokenStreamLength) { 809 return null; 810 } 811 return tokenStream.get(tokenStreamPointer++); 812 } 813 peekToken()814 private Token peekToken() { 815 if (tokenStreamPointer>=tokenStreamLength) { 816 return null; 817 } 818 return tokenStream.get(tokenStreamPointer); 819 } 820 raiseInternalException(int pos, SpelMessage message,Object... inserts)821 private void raiseInternalException(int pos, SpelMessage message,Object... inserts) { 822 throw new InternalParseException(new SpelParseException(expressionString,pos,message,inserts)); 823 } 824 toString(Token t)825 public String toString(Token t) { 826 if (t.getKind().hasPayload()) { 827 return t.stringValue(); 828 } else { 829 return t.kind.toString().toLowerCase(); 830 } 831 } 832 checkRightOperand(Token token, SpelNodeImpl operandExpression)833 private void checkRightOperand(Token token, SpelNodeImpl operandExpression) { 834 if (operandExpression==null) { 835 raiseInternalException(token.startpos,SpelMessage.RIGHT_OPERAND_PROBLEM); 836 } 837 } 838 839 /** 840 * Compress the start and end of a token into a single int 841 */ toPos(Token t)842 private int toPos(Token t) { 843 return (t.startpos<<16)+t.endpos; 844 } 845 toPos(int start,int end)846 private int toPos(int start,int end) { 847 return (start<<16)+end; 848 } 849 850 } 851