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