diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelNode.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelNode.java new file mode 100644 index 00000000000..dbfed4520fb --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelNode.java @@ -0,0 +1,85 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel; + +import org.springframework.expression.EvaluationException; + +/** + * Represents a node in the Ast for a parsed expression. + * + * @author Andy Clement + */ +public interface SpelNode { + + /** + * Evaluate the expression node in the context of the supplied expression state and return the value. + * + * @param expressionState the current expression state (includes the context) + * @return the value of this node evaluated against the specified state + */ + Object getValue(ExpressionState expressionState) throws EvaluationException; + + /** + * Determine if this expression node will support a setValue() call. + * + * @param expressionState the current expression state (includes the context) + * @return true if the expression node will allow setValue() + * @throws EvaluationException if something went wrong trying to determine if the node supports writing + */ + boolean isWritable(ExpressionState expressionState) throws EvaluationException; + + /** + * Evaluate the expression to a node and then set the new value on that node. For example, if the expression + * evaluates to a property reference then the property will be set to the new value. + * + * @param expressionState the current expression state (includes the context) + * @param newValue the new value + * @throws EvaluationException if any problem occurs evaluating the expression or setting the new value + */ + void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException; + + /** + * @return the string form of this AST node + */ + String toStringAST(); + + /** + * @return the number of children under this node + */ + int getChildCount(); + + /** + * Helper method that returns a SpelNode rather than an Antlr Tree node. + * + * @return the child node cast to a SpelNode + */ + SpelNode getChild(int index); + + /** + * Determine the class of the object passed in, unless it is already a class object. + * + * @param o the object that the caller wants the class of + * @return the class of the object if it is not already a class object, or null if the object is null + */ + Class getObjectClass(Object o); + + + /** + * @return the start position of this Ast node in the expression string + */ + public int getStartPosition(); + +} \ No newline at end of file diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedELException.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedELException.java new file mode 100644 index 00000000000..522b6cf4b1b --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedELException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel; + + +/** + * Wrap a checked SpelException temporarily so that it can be passed through some infrastructure code + * (for example Antlr) before being unwrapped at the top level. + * + * @author Andy Clement + */ +public class WrappedELException extends RuntimeException { + + public WrappedELException(SpelException e) { + super(e); + } + + @Override + public SpelException getCause() { + return (SpelException) super.getCause(); + } +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpelAntlrExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpelAntlrExpressionParser.java new file mode 100644 index 00000000000..a1399e75fef --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpelAntlrExpressionParser.java @@ -0,0 +1,75 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel.antlr; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.RecognitionException; +import org.springframework.expression.Expression; +import org.springframework.expression.ParseException; +import org.springframework.expression.ParserContext; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.SpelExpression; +import org.springframework.expression.spel.SpelNode; +import org.springframework.expression.spel.WrappedELException; +import org.springframework.expression.spel.SpelExpressionParser.SpelInternalParser; +import org.springframework.expression.spel.generated.SpringExpressionsLexer; +import org.springframework.expression.spel.generated.SpringExpressionsParser.expr_return; + +/** + * Wrap an Antlr lexer and parser. + * + * @author Andy Clement + */ +public class SpelAntlrExpressionParser implements SpelInternalParser { + + private final SpringExpressionsLexer lexer; + private final SpringExpressionsParserExtender parser; + + public SpelAntlrExpressionParser() { + lexer = new SpringExpressionsLexerExtender(); + CommonTokenStream tokens = new CommonTokenStream(lexer); + parser = new SpringExpressionsParserExtender(tokens); + } + + /** + * Parse an expression string. + * + * @param expressionString the expression to parse + * @param context the parser context in which to perform the parse + * @return a parsed expression object + * @throws ParseException if the expression is invalid + */ + public Expression doParseExpression(String expressionString, ParserContext context) throws ParseException { + try { + lexer.setCharStream(new ANTLRStringStream(expressionString)); + CommonTokenStream tokens = new CommonTokenStream(lexer); + parser.setTokenStream(tokens); + expr_return exprReturn = parser.expr(); + SpelExpression newExpression = new SpelExpression(expressionString, (SpelNode) exprReturn.getTree()); + return newExpression; + } catch (RecognitionException re) { + ParseException exception = new ParseException(expressionString, "Recognition error at position: " + + re.charPositionInLine + ": " + re.getMessage(), re); + throw exception; + } catch (WrappedELException e) { + SpelException wrappedException = e.getCause(); + throw new ParseException(expressionString, "Parsing problem: " + wrappedException.getMessage(), + wrappedException); + } + } + +} \ No newline at end of file diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsLexerExtender.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsLexerExtender.java new file mode 100644 index 00000000000..67d260a44dd --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsLexerExtender.java @@ -0,0 +1,82 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel.antlr; + +import org.antlr.runtime.RecognitionException; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.SpelMessages; +import org.springframework.expression.spel.WrappedELException; +import org.springframework.expression.spel.generated.SpringExpressionsLexer; + +public class SpringExpressionsLexerExtender extends SpringExpressionsLexer { + + public SpringExpressionsLexerExtender() { + super(); + } + + /** + * recover() attempts to provide better error messages once something has gone wrong. It then throws a + * InternalELException (has to be this unchecked exception as the exception must flow through Antlr lexer methods + * that do not have declared exceptions). The InternalELException will be caught at the top level and altered to + * include context (line,column) information before being rethrown.
+ * + * This error analysis code is in recover() rather than reportError() because reportError() isn't always called by + * the lexer and there is no way to add the calls to it by editing the .g file. + */ + @Override + public void recover(RecognitionException re) { + // TODO recovery needs an overhaul once the expression language syntax is agreed + + // List rules = getRuleInvocationStack(re, SpringExpressionsLexer.class.getName()); + // String failedRule = (String) rules.get(rules.size() - 1); + // System.out.println("DBG: lexer rule " + failedRule); + // need a concrete example of error recovery in here please! then i can delete the below + // if (re instanceof NoViableAltException) { + // NoViableAltException nvae = (NoViableAltException) re; + // // example error data: { "abc": def } + // if (failedRule.equals("mTokens") && Character.isLetter((char) (nvae.getUnexpectedType()))) { + // logger.error(ParserMessage.ERROR_STRINGS_MUST_BE_QUOTED, re.line, re.charPositionInLine); + // } + // + // } else if (re instanceof MismatchedRangeException) { + // // MismatchedRangeException mre = (MismatchedRangeException) re; + // // example error data: [ 123e ] + // if (failedRule.equals("mDIGIT") && rules.size() > 3 && ((String) rules.get(rules.size() - + // 3)).equals("mExponent")) { + // logger.error(ParserMessage.ERROR_INVALID_EXPONENT, re.line, re.charPositionInLine); + // } + // } else if (re instanceof MismatchedTokenException) { + // MismatchedTokenException mte = (MismatchedTokenException) re; + // logger.error(ParserMessage.ERROR_MISMATCHED_CHARACTER, mte.charPositionInLine, mte.charPositionInLine, + // getCharErrorDisplay(mte.expecting), getCharErrorDisplay(mte.c)); + // } + SpelException realException = new SpelException(re, SpelMessages.RECOGNITION_ERROR, re.toString()); + throw new WrappedELException(realException); + } + + @Override + public void reportError(RecognitionException re) { + // Do not report anything. If better messages could be reported they will have been reported + // by the recover() method above. + } + +// private String getTokenForId(int id) { +// if (id == -1) +// return "EOF"; +// return getTokenNames()[id]; +// } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsParserExtender.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsParserExtender.java new file mode 100644 index 00000000000..a7a7ce10ca9 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsParserExtender.java @@ -0,0 +1,90 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel.antlr; + +import org.antlr.runtime.BitSet; +import org.antlr.runtime.IntStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.SpelMessages; +import org.springframework.expression.spel.WrappedELException; +import org.springframework.expression.spel.ast.SpelTreeAdaptor; +import org.springframework.expression.spel.generated.SpringExpressionsParser; + +public class SpringExpressionsParserExtender extends SpringExpressionsParser { + + public SpringExpressionsParserExtender(TokenStream input) { + super(input); + setTreeAdaptor(new SpelTreeAdaptor()); + } + + /** + * Override super type implementation and just include the character position rather than the line number since the + * expressions are nearly all going to be just one line. + */ + @Override + public String getErrorHeader(RecognitionException e) { + StringBuilder retval = new StringBuilder(); + retval.append("(pos ").append(e.charPositionInLine).append("): "); + return retval.toString(); + } + + @Override + public void displayRecognitionError(String[] tokenNames, RecognitionException e) { + String message = getErrorMessage(e, tokenNames); + // TODO would something like this be worthwhile to improve messages? + // if (message.equals("no viable alternative at input ''") && !paraphrase.isEmpty()) { + // // This means we ran out of input building something, that something is named in paraphrase + // message = "no more input data to process whilst constructing " + paraphrase.peek(); + // } + SpelException parsingProblem = new SpelException(e.charPositionInLine, e, SpelMessages.PARSE_PROBLEM, message); + throw new WrappedELException(parsingProblem); + } + + /** + * Overridden purely because the base implementation does a System.err.println() + */ + @Override + public void recoverFromMismatchedToken(IntStream input, RecognitionException e, int ttype, BitSet follow) + throws RecognitionException { + // if next token is what we are looking for then "delete" this token + if (input.LA(2) == ttype) { + reportError(e); + /* + * System.err.println("recoverFromMismatchedToken deleting "+input.LT(1)+ " since "+input.LT(2)+" is what we + * want"); + */ + beginResync(); + input.consume(); // simply delete extra token + endResync(); + input.consume(); // move past ttype token as if all were ok + return; + } + if (!recoverFromMismatchedElement(input, e, follow)) { + throw e; + } + } + + @Override + public String getTokenErrorDisplay(Token t) { + if (t == null) { + return ""; + } + return super.getTokenErrorDisplay(t); + } +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/EmptySpelNode.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/EmptySpelNode.java new file mode 100644 index 00000000000..13ffe7bba2e --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/EmptySpelNode.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel.ast; + +import org.antlr.runtime.Token; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.ExpressionState; + +public class EmptySpelNode extends SpelNodeImpl { + + public EmptySpelNode(Token payload) { + super(payload); + } + + @Override + public Object getValueInternal(ExpressionState state) throws SpelException { + throw new RuntimeException("?"); + } + + @Override + public String toStringAST() { + return ""; + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java new file mode 100644 index 00000000000..db211416f68 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java @@ -0,0 +1,126 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel.ast; + +import java.io.Serializable; + +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTree; +import org.springframework.expression.EvaluationException; +import org.springframework.expression.common.ExpressionUtils; +import org.springframework.expression.spel.ExpressionState; +import org.springframework.expression.spel.SpelNode; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.SpelMessages; +import org.springframework.expression.spel.generated.SpringExpressionsParser; +import org.springframework.expression.spel.standard.StandardEvaluationContext; + +/** + * The common supertype of all AST nodes in a parsed Spring Expression Language format expression. + * + * @author Andy Clement + * + */ +public abstract class SpelNodeImpl extends CommonTree implements Serializable, SpelNode { + + /** + * The Antlr parser uses this constructor to build SpelNodes. + * + * @param payload the token for the node that has been parsed + */ + protected SpelNodeImpl(Token payload) { + super(payload); + } + + public final Object getValue(ExpressionState expressionState) throws EvaluationException { + if (expressionState==null) { + return getValue(new ExpressionState(new StandardEvaluationContext())); + } else { + return getValueInternal(expressionState); + } + } + + + + /* (non-Javadoc) + * @see org.springframework.expression.spel.ast.ISpelNode#getValue(org.springframework.expression.spel.ExpressionState) + */ + public abstract Object getValueInternal(ExpressionState expressionState) throws EvaluationException; + + /* (non-Javadoc) + * @see org.springframework.expression.spel.ast.ISpelNode#isWritable(org.springframework.expression.spel.ExpressionState) + */ + public boolean isWritable(ExpressionState expressionState) throws EvaluationException { + return false; + } + + /* (non-Javadoc) + * @see org.springframework.expression.spel.ast.ISpelNode#setValue(org.springframework.expression.spel.ExpressionState, java.lang.Object) + */ + public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException { + throw new SpelException(getCharPositionInLine(), SpelMessages.SETVALUE_NOT_SUPPORTED, getClass(), + getTokenName()); + } + + /** + * @return return the token this node represents + */ + protected String getTokenName() { + if (getToken() == null) { + return "UNKNOWN"; + } + return SpringExpressionsParser.tokenNames[getToken().getType()]; + } + + /* (non-Javadoc) + * @see org.springframework.expression.spel.ast.ISpelNode#toStringAST() + */ + public abstract String toStringAST(); + + /* (non-Javadoc) + * @see org.springframework.expression.spel.ast.ISpelNode#getChild(int) + */ + @Override + public SpelNodeImpl getChild(int index) { + return (SpelNodeImpl) super.getChild(index); + } + + /* (non-Javadoc) + * @see org.springframework.expression.spel.ast.ISpelNode#getObjectClass(java.lang.Object) + */ + public Class getObjectClass(Object o) { + if (o == null) + return null; + return (o instanceof Class) ? ((Class) o) : o.getClass(); + } + + protected final Object getValue(ExpressionState state, Class desiredReturnType) throws EvaluationException { + Object result = getValueInternal(state); + if (result != null && desiredReturnType != null) { + Class resultType = result.getClass(); + if (desiredReturnType.isAssignableFrom(resultType)) { + return result; + } + // Attempt conversion to the requested type, may throw an exception + return ExpressionUtils.convert(state.getEvaluationContext(), result, desiredReturnType); + } + return result; + } + + public int getStartPosition() { + return getCharPositionInLine(); + } +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java new file mode 100644 index 00000000000..42866b1f68d --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java @@ -0,0 +1,134 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.expression.spel.ast; + +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTreeAdaptor; +import org.springframework.expression.spel.generated.SpringExpressionsLexer; + +public class SpelTreeAdaptor extends CommonTreeAdaptor { + @Override + public Object create(Token payload) { + if (payload != null) { + switch (payload.getType()) { + + case SpringExpressionsLexer.TRUE: + return new BooleanLiteral(payload, true); + case SpringExpressionsLexer.FALSE: + return new BooleanLiteral(payload, false); + + case SpringExpressionsLexer.OR: + return new OperatorOr(payload); + case SpringExpressionsLexer.AND: + return new OperatorAnd(payload); + case SpringExpressionsLexer.BANG: + return new OperatorNot(payload); + + case SpringExpressionsLexer.REAL_LITERAL: + return new RealLiteral(payload); + case SpringExpressionsLexer.INTEGER_LITERAL: + return Literal.getIntLiteral(payload, 10); + case SpringExpressionsLexer.HEXADECIMAL_INTEGER_LITERAL: + return Literal.getIntLiteral(payload, 16); + + case SpringExpressionsLexer.NOT_EQUAL: + return new OperatorInequality(payload); + case SpringExpressionsLexer.EQUAL: + return new OperatorEquality(payload); + case SpringExpressionsLexer.GREATER_THAN: + return new OperatorGreaterThan(payload); + case SpringExpressionsLexer.LESS_THAN: + return new OperatorLessThan(payload); + case SpringExpressionsLexer.LESS_THAN_OR_EQUAL: + return new OperatorLessThanOrEqual(payload); + case SpringExpressionsLexer.GREATER_THAN_OR_EQUAL: + return new OperatorGreaterThanOrEqual(payload); + case SpringExpressionsLexer.PLUS: + return new OperatorPlus(payload); + case SpringExpressionsLexer.MINUS: + return new OperatorMinus(payload); + case SpringExpressionsLexer.STAR/* MULTIPLY */: + return new OperatorMultiply(payload); + case SpringExpressionsLexer.DIV/* DIVIDE */: + return new OperatorDivide(payload); + case SpringExpressionsLexer.MOD: + return new OperatorModulus(payload); + + case SpringExpressionsLexer.STRING_LITERAL: + case SpringExpressionsLexer.DQ_STRING_LITERAL: + return new StringLiteral(payload); + case SpringExpressionsLexer.NULL_LITERAL: + return new NullLiteral(payload); + + case SpringExpressionsLexer.ID: + return new Identifier(payload); + case SpringExpressionsLexer.PROPERTY_OR_FIELD: + return new PropertyOrFieldReference(payload); + case SpringExpressionsLexer.METHOD: + return new MethodReference(payload); + case SpringExpressionsLexer.QUALIFIED_IDENTIFIER: + return new QualifiedIdentifier(payload); + case SpringExpressionsLexer.TYPEREF: + return new TypeReference(payload); + + case SpringExpressionsLexer.EXPRESSION: + return new CompoundExpression(payload); + + case SpringExpressionsLexer.CONSTRUCTOR: + return new ConstructorReference(payload, false); + case SpringExpressionsLexer.VARIABLEREF: + return new VariableReference(payload); + case SpringExpressionsLexer.FUNCTIONREF: + return new FunctionReference(payload); + case SpringExpressionsLexer.PROJECT: + return new Projection(payload); + case SpringExpressionsLexer.SELECT: + return new Selection(payload, Selection.ALL); + case SpringExpressionsLexer.SELECT_FIRST: + return new Selection(payload, Selection.FIRST); + case SpringExpressionsLexer.SELECT_LAST: + return new Selection(payload, Selection.LAST); + + case SpringExpressionsLexer.ASSIGN: + return new Assign(payload); + case SpringExpressionsLexer.QMARK: + return new Ternary(payload); + case SpringExpressionsLexer.INDEXER: + return new Indexer(payload); + + case SpringExpressionsLexer.BETWEEN: + return new OperatorBetween(payload); + case SpringExpressionsLexer.MATCHES: + return new OperatorMatches(payload); + case SpringExpressionsLexer.INSTANCEOF: + return new OperatorInstanceof(payload); + + case SpringExpressionsLexer.RPAREN: + return new Placeholder(payload); + case SpringExpressionsLexer.COLON: + return new Placeholder(payload); + + case SpringExpressionsLexer.DOT: + return new Dot(payload); + + default: + throw new RuntimeException("Not implemented for '" + payload + "' " + getToken(payload) + "' " + + payload.getType()); + } + } + return new EmptySpelNode(payload); + } +} \ No newline at end of file