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