105 changed files with 3622 additions and 8636 deletions
@ -1,13 +1,12 @@
@@ -1,13 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<classpath> |
||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/> |
||||
<classpathentry kind="src" output="target/classes" path="src/main/resources"/> |
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/> |
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/resources"/> |
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> |
||||
<classpathentry kind="var" path="IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar" sourcepath="/IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-sources-1.1.1.jar"/> |
||||
<classpathentry kind="var" path="IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-4.5.0.jar" sourcepath="/IVY_CACHE/org.junit/com.springsource.junit/3.8.2/com.springsource.junit-sources-3.8.2.jar"/> |
||||
<classpathentry kind="var" path="IVY_CACHE/org.antlr/com.springsource.org.antlr/3.0.1/com.springsource.org.antlr-3.0.1.jar" sourcepath="/IVY_CACHE/org.antlr/com.springsource.org.antlr/3.0.1/com.springsource.org.antlr-sources-3.0.1.jar"/> |
||||
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.core"/> |
||||
<classpathentry kind="output" path="target/classes"/> |
||||
</classpath> |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<classpath> |
||||
<classpathentry kind="src" output="target/classes" path="src/main/java"/> |
||||
<classpathentry kind="src" output="target/classes" path="src/main/resources"/> |
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/> |
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/resources"/> |
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> |
||||
<classpathentry kind="var" path="IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar" sourcepath="/IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-sources-1.1.1.jar"/> |
||||
<classpathentry kind="var" path="IVY_CACHE/org.junit/com.springsource.org.junit/4.5.0/com.springsource.org.junit-4.5.0.jar" sourcepath="/IVY_CACHE/org.junit/com.springsource.junit/3.8.2/com.springsource.junit-sources-3.8.2.jar"/> |
||||
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.core"/> |
||||
<classpathentry kind="output" path="target/classes"/> |
||||
</classpath> |
||||
|
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/* |
||||
* Copyright 2002-2009 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; |
||||
|
||||
|
||||
/** |
||||
* Super class for exceptions that can occur whilst processing expressions |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
public class ExpressionException extends Exception { |
||||
|
||||
protected String expressionString; |
||||
protected int position; // -1 if not known - but should be known in all reasonable cases
|
||||
|
||||
/** |
||||
* Creates a new expression exception. |
||||
* @param expressionString the expression string |
||||
* @param message a descriptive message |
||||
*/ |
||||
public ExpressionException(String expressionString, String message) { |
||||
super(message); |
||||
this.position = -1; |
||||
this.expressionString = expressionString; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new expression exception. |
||||
* @param expressionString the expression string |
||||
* @param position the position in the expression string where the problem occurred |
||||
* @param message a descriptive message |
||||
*/ |
||||
public ExpressionException(String expressionString, int position, String message) { |
||||
super(message); |
||||
this.position = position; |
||||
this.expressionString = expressionString; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new expression exception. |
||||
* @param position the position in the expression string where the problem occurred |
||||
* @param message a descriptive message |
||||
*/ |
||||
public ExpressionException(int position, String message) { |
||||
super(message); |
||||
this.position = position; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new expression exception. |
||||
* @param position the position in the expression string where the problem occurred |
||||
* @param message a descriptive message |
||||
* @param cause the underlying cause of this exception |
||||
*/ |
||||
public ExpressionException(int position, String message, Throwable cause) { |
||||
super(message,cause); |
||||
this.position = position; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new expression exception. |
||||
* @param message a descriptive message |
||||
*/ |
||||
public ExpressionException(String message) { |
||||
super(message); |
||||
} |
||||
|
||||
public ExpressionException(String message, Throwable cause) { |
||||
super(message,cause); |
||||
} |
||||
|
||||
public String toDetailedString() { |
||||
StringBuilder output = new StringBuilder(); |
||||
if (expressionString!=null) { |
||||
output.append("Expression '"); |
||||
output.append(expressionString); |
||||
output.append("'"); |
||||
if (position!=-1) { |
||||
output.append(" @ "); |
||||
output.append(position); |
||||
} |
||||
output.append(": "); |
||||
} |
||||
output.append(getMessage()); |
||||
return output.toString(); |
||||
} |
||||
|
||||
public final String getExpressionString() { |
||||
return this.expressionString; |
||||
} |
||||
|
||||
public final int getPosition() { |
||||
return position; |
||||
} |
||||
|
||||
} |
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,107 @@
@@ -0,0 +1,107 @@
|
||||
/* |
||||
* Copyright 2004-2009 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.ParseException; |
||||
|
||||
|
||||
/** |
||||
* Root exception for Spring EL related exceptions. Rather than holding a hard coded string indicating the problem, it |
||||
* records a message key and the inserts for the message. See {@link SpelMessages} for the list of all possible messages |
||||
* that can occur. |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
public class SpelParseException extends ParseException { |
||||
|
||||
private SpelMessages message; |
||||
private Object[] inserts; |
||||
|
||||
// public SpelParseException(String expressionString, int position, Throwable cause, SpelMessages message, Object... inserts) {
|
||||
// super(expressionString, position, message.formatMessage(position,inserts), cause);
|
||||
// this.message = message;
|
||||
// this.inserts = inserts;
|
||||
// }
|
||||
|
||||
public SpelParseException(String expressionString, int position, SpelMessages message, Object... inserts) { |
||||
super(expressionString, position, message.formatMessage(position,inserts)); |
||||
this.position = position; |
||||
this.message = message; |
||||
this.inserts = inserts; |
||||
} |
||||
|
||||
public SpelParseException(int position, SpelMessages message, Object... inserts) { |
||||
super(position, message.formatMessage(position,inserts)); |
||||
this.position = position; |
||||
this.message = message; |
||||
this.inserts = inserts; |
||||
} |
||||
|
||||
public SpelParseException(int position, Throwable cause, SpelMessages message, Object... inserts) { |
||||
super(position, message.formatMessage(position,inserts), cause); |
||||
this.position = position; |
||||
this.message = message; |
||||
this.inserts = inserts; |
||||
} |
||||
|
||||
//
|
||||
// public SpelException(Throwable cause, SpelMessages message, Object... inserts) {
|
||||
// super(cause);
|
||||
// this.message = message;
|
||||
// this.inserts = inserts;
|
||||
// }
|
||||
//
|
||||
// public SpelException(int position, SpelMessages message, Object... inserts) {
|
||||
// super((Throwable)null);
|
||||
// this.position = position;
|
||||
// this.message = message;
|
||||
// this.inserts = inserts;
|
||||
// }
|
||||
//
|
||||
// public SpelException(SpelMessages message, Object... inserts) {
|
||||
// super((Throwable)null);
|
||||
// this.message = message;
|
||||
// this.inserts = inserts;
|
||||
// }
|
||||
|
||||
|
||||
/** |
||||
* @return a formatted message with inserts applied |
||||
*/ |
||||
@Override |
||||
public String getMessage() { |
||||
if (message != null) |
||||
return message.formatMessage(position, inserts); |
||||
else |
||||
return super.getMessage(); |
||||
} |
||||
|
||||
/** |
||||
* @return the unformatted message |
||||
*/ |
||||
public SpelMessages getMessageUnformatted() { |
||||
return this.message; |
||||
} |
||||
|
||||
/** |
||||
* @return the message inserts |
||||
*/ |
||||
public Object[] getInserts() { |
||||
return inserts; |
||||
} |
||||
|
||||
} |
||||
@ -1,79 +0,0 @@
@@ -1,79 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2009 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.common.TemplateAwareExpressionParser; |
||||
import org.springframework.expression.spel.SpelException; |
||||
import org.springframework.expression.spel.SpelExpression; |
||||
import org.springframework.expression.spel.SpelNode; |
||||
import org.springframework.expression.spel.WrappedSpelException; |
||||
import org.springframework.expression.spel.generated.SpringExpressionsLexer; |
||||
import org.springframework.expression.spel.generated.SpringExpressionsParser.expr_return; |
||||
|
||||
/** |
||||
* Default {@link org.springframework.expression.ExpressionParser} implementation, |
||||
* wrapping an Antlr lexer and parser that implements standard Spring EL syntax. |
||||
* |
||||
* @author Andy Clement |
||||
* @author Juergen Hoeller |
||||
* @since 3.0 |
||||
*/ |
||||
public class SpelAntlrExpressionParser extends TemplateAwareExpressionParser { |
||||
|
||||
private final SpringExpressionsLexer lexer; |
||||
|
||||
private final SpringExpressionsParserExtender parser; |
||||
|
||||
|
||||
public SpelAntlrExpressionParser() { |
||||
this.lexer = new SpringExpressionsLexerExtender(); |
||||
CommonTokenStream tokens = new CommonTokenStream(this.lexer); |
||||
this.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 |
||||
*/ |
||||
protected Expression doParseExpression(String expressionString, ParserContext context) throws ParseException { |
||||
try { |
||||
this.lexer.setCharStream(new ANTLRStringStream(expressionString)); |
||||
CommonTokenStream tokens = new CommonTokenStream(this.lexer); |
||||
this.parser.setTokenStream(tokens); |
||||
expr_return exprReturn = this.parser.expr(); |
||||
return new SpelExpression(expressionString, (SpelNode) exprReturn.getTree()); |
||||
} catch (RecognitionException re) { |
||||
throw new ParseException(expressionString, |
||||
"Recognition error at position: " + re.charPositionInLine + ": " + re.getMessage(), re); |
||||
} catch (WrappedSpelException ex) { |
||||
SpelException wrappedException = ex.getCause(); |
||||
throw new ParseException(expressionString, |
||||
"Parsing problem: " + wrappedException.getMessage(), wrappedException); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,83 +0,0 @@
@@ -1,83 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2009 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.WrappedSpelException; |
||||
import org.springframework.expression.spel.generated.SpringExpressionsLexer; |
||||
|
||||
/** |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
class SpringExpressionsLexerExtender extends SpringExpressionsLexer { |
||||
|
||||
/** |
||||
* 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.<br> |
||||
* |
||||
* 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 WrappedSpelException(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];
|
||||
// }
|
||||
|
||||
} |
||||
@ -1,95 +0,0 @@
@@ -1,95 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2009 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.WrappedSpelException; |
||||
import org.springframework.expression.spel.ast.SpelTreeAdaptor; |
||||
import org.springframework.expression.spel.generated.SpringExpressionsParser; |
||||
|
||||
/** |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
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 '<EOF>'") && !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 WrappedSpelException(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 "<unknown>"; |
||||
} |
||||
return super.getTokenErrorDisplay(t); |
||||
} |
||||
} |
||||
@ -1,42 +1,25 @@
@@ -1,42 +1,25 @@
|
||||
/* |
||||
* Copyright 2002-2009 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.TypedValue; |
||||
|
||||
/** |
||||
* Expression language AST node that represents an integer literal. |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
public class IntLiteral extends Literal { |
||||
|
||||
private final TypedValue value; |
||||
|
||||
IntLiteral(Token payload, int value) { |
||||
super(payload); |
||||
this.value = new TypedValue(value, INTEGER_TYPE_DESCRIPTOR); |
||||
} |
||||
|
||||
@Override |
||||
public TypedValue getLiteralValue() { |
||||
return this.value; |
||||
} |
||||
|
||||
} |
||||
package org.springframework.expression.spel.ast; |
||||
|
||||
import org.springframework.expression.TypedValue; |
||||
|
||||
/** |
||||
* Expression language AST node that represents an integer literal. |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
public class IntLiteral extends Literal { |
||||
|
||||
private final TypedValue value; |
||||
|
||||
IntLiteral(String payload, int pos, int value) { |
||||
super(payload, pos); |
||||
this.value = new TypedValue(value, INTEGER_TYPE_DESCRIPTOR); |
||||
} |
||||
|
||||
@Override |
||||
public TypedValue getLiteralValue() { |
||||
return this.value; |
||||
} |
||||
|
||||
} |
||||
|
||||
@ -1,139 +0,0 @@
@@ -1,139 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2009 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; |
||||
|
||||
/** |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
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.POWER: |
||||
return new OperatorPower(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); |
||||
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.DOT: |
||||
return new Dot(payload); |
||||
|
||||
default: |
||||
throw new RuntimeException("Not implemented for '" + payload + "' " + getToken(payload) + "' " |
||||
+ payload.getType()); |
||||
} |
||||
} |
||||
return new EmptySpelNode(payload); |
||||
} |
||||
|
||||
} |
||||
@ -1,74 +0,0 @@
@@ -1,74 +0,0 @@
|
||||
SIGN=76 |
||||
DOLLAR=71 |
||||
DECIMAL_DIGIT=52 |
||||
APOS=68 |
||||
TYPEREF=13 |
||||
HEXADECIMAL_INTEGER_LITERAL=48 |
||||
STAR=29 |
||||
MOD=31 |
||||
VARIABLEREF=14 |
||||
AND=26 |
||||
ID=36 |
||||
SUBTRACT=17 |
||||
UPTO=73 |
||||
LPAREN=23 |
||||
TYPE=44 |
||||
HOLDER=10 |
||||
AT=72 |
||||
LBRACKET=38 |
||||
QUALIFIED_IDENTIFIER=6 |
||||
RPAREN=24 |
||||
STRING_LITERAL=45 |
||||
MATCHES=63 |
||||
REAL_LITERAL=49 |
||||
NOT_EQUAL=56 |
||||
COMMA=37 |
||||
FUNCTIONREF=12 |
||||
EQUAL=55 |
||||
PIPE=67 |
||||
PLUS=27 |
||||
RBRACKET=39 |
||||
DOT=34 |
||||
SELECT=41 |
||||
EXPRESSION=5 |
||||
ADD=16 |
||||
LESS_THAN_OR_EQUAL=58 |
||||
GREATER_THAN=59 |
||||
POUND=35 |
||||
PROJECT=40 |
||||
SELECT_LAST=43 |
||||
DEFAULT=20 |
||||
NUMBER=18 |
||||
REAL_TYPE_SUFFIX=75 |
||||
HEX_DIGIT=54 |
||||
POWER=32 |
||||
LCURLY=65 |
||||
NULL_LITERAL=47 |
||||
PROPERTY_OR_FIELD=7 |
||||
BANG=33 |
||||
INSTANCEOF=61 |
||||
MINUS=28 |
||||
SEMI=64 |
||||
TRUE=50 |
||||
COLON=22 |
||||
GREATER_THAN_OR_EQUAL=60 |
||||
WS=70 |
||||
DOT_ESCAPED=69 |
||||
DQ_STRING_LITERAL=46 |
||||
INTEGER_LITERAL=4 |
||||
RCURLY=66 |
||||
INDEXER=8 |
||||
OR=25 |
||||
LESS_THAN=57 |
||||
ASSIGN=19 |
||||
NAMED_ARGUMENT=11 |
||||
SELECT_FIRST=42 |
||||
DIV=30 |
||||
FALSE=51 |
||||
EXPONENT_PART=74 |
||||
BETWEEN=62 |
||||
INTEGER_TYPE_SUFFIX=53 |
||||
METHOD=15 |
||||
CONSTRUCTOR=9 |
||||
QMARK=21 |
||||
'new'=77 |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,142 +0,0 @@
@@ -1,142 +0,0 @@
|
||||
lexer grammar SpringExpressions; |
||||
options { |
||||
language=Java; |
||||
|
||||
} |
||||
@header {package org.springframework.expression.spel.generated;} |
||||
|
||||
T77 : 'new' ; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 183 |
||||
INTEGER_LITERAL |
||||
: (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 186 |
||||
HEXADECIMAL_INTEGER_LITERAL : ('0x' | '0X') (HEX_DIGIT)+ (INTEGER_TYPE_SUFFIX)?; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 200 |
||||
ASSIGN: '='; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 201 |
||||
EQUAL: '=='; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 202 |
||||
NOT_EQUAL: '!='; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 203 |
||||
LESS_THAN: '<'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 204 |
||||
LESS_THAN_OR_EQUAL: '<='; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 205 |
||||
GREATER_THAN: '>'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 206 |
||||
GREATER_THAN_OR_EQUAL: '>='; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 207 |
||||
INSTANCEOF: 'instanceof'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 208 |
||||
BETWEEN:'between'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 209 |
||||
MATCHES:'matches'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 210 |
||||
NULL_LITERAL: 'null'; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 212 |
||||
SEMI: ';'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 213 |
||||
DOT: '.'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 214 |
||||
COMMA: ','; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 215 |
||||
LPAREN: '('; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 216 |
||||
RPAREN: ')'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 217 |
||||
LCURLY: '{'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 218 |
||||
RCURLY: '}'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 219 |
||||
LBRACKET: '['; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 220 |
||||
RBRACKET: ']'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 221 |
||||
PIPE: '|'; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 223 |
||||
AND: 'and'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 224 |
||||
OR: 'or'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 225 |
||||
FALSE: 'false'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 226 |
||||
TRUE: 'true'; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 228 |
||||
PLUS: '+'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 229 |
||||
MINUS: '-'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 230 |
||||
DIV: '/'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 231 |
||||
STAR: '*'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 232 |
||||
MOD: '%'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 233 |
||||
POWER: '^'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 234 |
||||
BANG: '!'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 235 |
||||
POUND: '#'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 236 |
||||
QMARK: '?'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 237 |
||||
DEFAULT: '??'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 238 |
||||
PROJECT: '!['; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 239 |
||||
SELECT: '?['; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 240 |
||||
SELECT_FIRST: '^['; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 241 |
||||
SELECT_LAST: '$['; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 242 |
||||
TYPE: 'T('; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 244 |
||||
STRING_LITERAL: '\''! (APOS|~'\'')* '\''!; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 245 |
||||
DQ_STRING_LITERAL: '"'! (~'"')* '"'!; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 246 |
||||
ID: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 247 |
||||
DOT_ESCAPED: '\\.'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 248 |
||||
WS: ( ' ' | '\t' | '\n' |'\r')+ { $channel=HIDDEN; } ; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 249 |
||||
DOLLAR: '$'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 250 |
||||
AT: '@'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 251 |
||||
UPTO: '..'; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 252 |
||||
COLON: ':'; |
||||
|
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 255 |
||||
REAL_LITERAL : |
||||
('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | |
||||
((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | |
||||
((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) | |
||||
((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)); |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 261 |
||||
fragment APOS : '\''! '\''; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 262 |
||||
fragment DECIMAL_DIGIT : '0'..'9' ; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 263 |
||||
fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' ); |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 264 |
||||
fragment HEX_DIGIT : '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f'; |
||||
|
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 266 |
||||
fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* (DECIMAL_DIGIT)+ ; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 267 |
||||
fragment SIGN : '+' | '-' ; |
||||
// $ANTLR src "F:\svn\sfw2\org.springframework.expression\src\main\java\org\springframework\expression\spel\generated\SpringExpressions.g" 268 |
||||
fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd'; |
||||
@ -0,0 +1,745 @@
@@ -0,0 +1,745 @@
|
||||
/* |
||||
* Copyright 2008-2009 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.standard; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Stack; |
||||
|
||||
import org.springframework.expression.ParseException; |
||||
import org.springframework.expression.ParserContext; |
||||
import org.springframework.expression.common.TemplateAwareExpressionParser; |
||||
import org.springframework.expression.spel.SpelExpression; |
||||
import org.springframework.expression.spel.SpelMessages; |
||||
import org.springframework.expression.spel.SpelNode; |
||||
import org.springframework.expression.spel.SpelParseException; |
||||
import org.springframework.expression.spel.ast.Assign; |
||||
import org.springframework.expression.spel.ast.BooleanLiteral; |
||||
import org.springframework.expression.spel.ast.CompoundExpression; |
||||
import org.springframework.expression.spel.ast.ConstructorReference; |
||||
import org.springframework.expression.spel.ast.Elvis; |
||||
import org.springframework.expression.spel.ast.FunctionReference; |
||||
import org.springframework.expression.spel.ast.Identifier; |
||||
import org.springframework.expression.spel.ast.Indexer; |
||||
import org.springframework.expression.spel.ast.Literal; |
||||
import org.springframework.expression.spel.ast.MethodReference; |
||||
import org.springframework.expression.spel.ast.NullLiteral; |
||||
import org.springframework.expression.spel.ast.OpEQ; |
||||
import org.springframework.expression.spel.ast.OpGE; |
||||
import org.springframework.expression.spel.ast.OpGT; |
||||
import org.springframework.expression.spel.ast.OpLE; |
||||
import org.springframework.expression.spel.ast.OpLT; |
||||
import org.springframework.expression.spel.ast.OpNE; |
||||
import org.springframework.expression.spel.ast.OperatorAnd; |
||||
import org.springframework.expression.spel.ast.OperatorDivide; |
||||
import org.springframework.expression.spel.ast.OperatorInstanceof; |
||||
import org.springframework.expression.spel.ast.OperatorMatches; |
||||
import org.springframework.expression.spel.ast.OperatorMinus; |
||||
import org.springframework.expression.spel.ast.OperatorModulus; |
||||
import org.springframework.expression.spel.ast.OperatorMultiply; |
||||
import org.springframework.expression.spel.ast.OperatorNot; |
||||
import org.springframework.expression.spel.ast.OperatorOr; |
||||
import org.springframework.expression.spel.ast.OperatorPlus; |
||||
import org.springframework.expression.spel.ast.OperatorPower; |
||||
import org.springframework.expression.spel.ast.Projection; |
||||
import org.springframework.expression.spel.ast.PropertyOrFieldReference; |
||||
import org.springframework.expression.spel.ast.QualifiedIdentifier; |
||||
import org.springframework.expression.spel.ast.Selection; |
||||
import org.springframework.expression.spel.ast.SpelNodeImpl; |
||||
import org.springframework.expression.spel.ast.StringLiteral; |
||||
import org.springframework.expression.spel.ast.Ternary; |
||||
import org.springframework.expression.spel.ast.TypeReference; |
||||
import org.springframework.expression.spel.ast.VariableReference; |
||||
|
||||
|
||||
/** |
||||
* Hand written SpEL parser. Instances are reusable. |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
public class SpelExpressionParser extends TemplateAwareExpressionParser { |
||||
|
||||
// The expression being parsed
|
||||
private String expressionString; |
||||
|
||||
// The token stream constructed from that expression string
|
||||
private List<Token> tokenStream; |
||||
|
||||
// length of a populated token stream
|
||||
private int tokenStreamLength; |
||||
|
||||
// Current location in the token stream when processing tokens
|
||||
private int tokenStreamPointer; |
||||
|
||||
// For rules that build nodes, they are stacked here for return
|
||||
private Stack<SpelNodeImpl> constructedNodes = new Stack<SpelNodeImpl>(); |
||||
|
||||
public SpelExpressionParser() { |
||||
} |
||||
|
||||
public SpelExpression parse(String expressionString) throws ParseException { |
||||
return doParseExpression(expressionString, null); |
||||
} |
||||
|
||||
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException { |
||||
try { |
||||
Tokenizer tokenizer = new Tokenizer(expressionString); |
||||
tokenizer.process(); |
||||
this.expressionString = expressionString; |
||||
tokenStream = tokenizer.getTokens(); |
||||
tokenStreamLength = tokenStream.size(); |
||||
tokenStreamPointer = 0; |
||||
constructedNodes.clear(); |
||||
SpelNode ast = eatExpression(); |
||||
if (moreTokens()) { |
||||
throw new SpelParseException(peekToken().startpos,SpelMessages.MORE_INPUT,toString(nextToken())); |
||||
} |
||||
assert constructedNodes.isEmpty(); |
||||
return new SpelExpression(expressionString,ast); |
||||
} catch (InternalParseException ipe) { |
||||
throw ipe.getCause(); |
||||
} |
||||
} |
||||
|
||||
public String toString(Token t) { |
||||
if (t.getKind().hasPayload()) { |
||||
return t.stringValue(); |
||||
} else { |
||||
return t.kind.toString().toLowerCase(); |
||||
} |
||||
} |
||||
|
||||
// expression
|
||||
// : logicalOrExpression
|
||||
// ( (ASSIGN^ logicalOrExpression)
|
||||
// | (DEFAULT^ logicalOrExpression)
|
||||
// | (QMARK^ expression COLON! expression))?;
|
||||
private SpelNodeImpl eatExpression() { |
||||
SpelNodeImpl expr = eatLogicalOrExpression(); |
||||
if (moreTokens()) { |
||||
Token t = peekToken(); |
||||
if (t.kind==TokenKind.ASSIGN) { // a=b
|
||||
nextToken(); |
||||
SpelNodeImpl assignedValue = eatLogicalOrExpression(); |
||||
return new Assign(toPos(t),expr,assignedValue); |
||||
} else if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
|
||||
nextToken(); // elvis has left the building
|
||||
SpelNodeImpl valueIfNull = eatLogicalOrExpression(); |
||||
return new Elvis(toPos(t),expr,valueIfNull); |
||||
} else if (t.kind==TokenKind.QMARK) { // a?b:c
|
||||
nextToken(); |
||||
SpelNodeImpl ifTrueExprValue = eatLogicalOrExpression(); |
||||
eatToken(TokenKind.COLON); |
||||
SpelNodeImpl ifFalseExprValue = eatLogicalOrExpression(); |
||||
return new Ternary(toPos(t),expr,ifTrueExprValue,ifFalseExprValue); |
||||
} |
||||
} |
||||
return expr; |
||||
} |
||||
|
||||
//logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*;
|
||||
private SpelNodeImpl eatLogicalOrExpression() { |
||||
SpelNodeImpl expr = eatLogicalAndExpression(); |
||||
while (peekIdentifierToken("or")) { |
||||
Token t = nextToken();//consume OR
|
||||
SpelNodeImpl expr2 = eatLogicalAndExpression(); |
||||
checkRightOperand(t,expr2); |
||||
expr = new OperatorOr(toPos(t),expr,expr2); |
||||
} |
||||
return expr; |
||||
} |
||||
|
||||
private void checkRightOperand(Token token, SpelNodeImpl operandExpression) { |
||||
if (operandExpression==null) { |
||||
throw new InternalParseException(new SpelParseException(token.startpos,SpelMessages.RIGHT_OPERAND_PROBLEM)); |
||||
} |
||||
} |
||||
|
||||
//logicalAndExpression : relationalExpression (AND^ relationalExpression)*;
|
||||
private SpelNodeImpl eatLogicalAndExpression() { |
||||
SpelNodeImpl expr = eatRelationalExpression(); |
||||
while (peekIdentifierToken("and")) { |
||||
Token t = nextToken();// consume 'AND'
|
||||
SpelNodeImpl rightExpr = eatRelationalExpression(); |
||||
checkRightOperand(t,rightExpr); |
||||
expr = new OperatorAnd(toPos(t),expr,rightExpr); |
||||
} |
||||
return expr; |
||||
} |
||||
|
||||
//relationalExpression : sumExpression (relationalOperator^ sumExpression)?;
|
||||
private SpelNodeImpl eatRelationalExpression() { |
||||
SpelNodeImpl expr = eatSumExpression(); |
||||
Token relationalOperatorToken = maybeEatRelationalOperator(); |
||||
if (relationalOperatorToken!=null) { |
||||
Token t = nextToken();//consume relational operator token
|
||||
SpelNodeImpl expr2 = eatSumExpression(); |
||||
checkRightOperand(t,expr2); |
||||
if (relationalOperatorToken.isNumericRelationalOperator()) { |
||||
if (relationalOperatorToken.isGreaterThan()) { |
||||
return new OpGT(toPos(t),expr,expr2); |
||||
} else if (relationalOperatorToken.isLessThan()) { |
||||
return new OpLT(toPos(t),expr,expr2); |
||||
} else if (relationalOperatorToken.isLessThanOrEqual()) { |
||||
return new OpLE(toPos(t),expr,expr2); |
||||
} else if (relationalOperatorToken.isGreaterThanOrEqual()) { |
||||
return new OpGE(toPos(t),expr,expr2); |
||||
} else if (relationalOperatorToken.isEquality()) { |
||||
return new OpEQ(toPos(t),expr,expr2); |
||||
} else { |
||||
assert relationalOperatorToken.kind==TokenKind.NE; |
||||
return new OpNE(toPos(t),expr,expr2); |
||||
} |
||||
} |
||||
if (relationalOperatorToken.kind==TokenKind.INSTANCEOF) { |
||||
return new OperatorInstanceof(toPos(t),expr,expr2); |
||||
} else if (relationalOperatorToken.kind==TokenKind.MATCHES) { |
||||
return new OperatorMatches(toPos(t),expr,expr2); |
||||
} else { |
||||
assert relationalOperatorToken.kind==TokenKind.BETWEEN; |
||||
return new org.springframework.expression.spel.ast.OperatorBetween(toPos(t),expr,expr2); |
||||
} |
||||
} |
||||
return expr; |
||||
} |
||||
|
||||
//sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*;
|
||||
private SpelNodeImpl eatSumExpression() { |
||||
SpelNodeImpl expr = eatProductExpression(); |
||||
while (peekToken(TokenKind.PLUS,TokenKind.MINUS)) { |
||||
Token t = nextToken();//consume PLUS or MINUS
|
||||
SpelNodeImpl rhOperand = eatProductExpression(); |
||||
checkRightOperand(t,rhOperand); |
||||
if (t.getKind()==TokenKind.PLUS) { |
||||
expr = new OperatorPlus(toPos(t),expr,rhOperand); |
||||
} else { |
||||
expr = new OperatorMinus(toPos(t),expr,rhOperand); |
||||
} |
||||
} |
||||
return expr; |
||||
} |
||||
|
||||
//productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ;
|
||||
private SpelNodeImpl eatProductExpression() { |
||||
SpelNodeImpl expr = eatPowerExpression(); |
||||
while (peekToken(TokenKind.STAR,TokenKind.DIV,TokenKind.MOD)) { |
||||
Token t = nextToken(); // consume STAR/DIV/MOD
|
||||
SpelNodeImpl expr2 = eatPowerExpression(); |
||||
checkRightOperand(t,expr2); |
||||
if (t.getKind()==TokenKind.STAR) { |
||||
expr = new OperatorMultiply(toPos(t),expr,expr2); |
||||
} else if (t.getKind()==TokenKind.DIV) { |
||||
expr = new OperatorDivide(toPos(t),expr,expr2); |
||||
} else { |
||||
expr = new OperatorModulus(toPos(t),expr,expr2); |
||||
} |
||||
} |
||||
return expr; |
||||
} |
||||
|
||||
//powerExpr : unaryExpression (POWER^ unaryExpression)? ;
|
||||
private SpelNodeImpl eatPowerExpression() { |
||||
SpelNodeImpl expr = eatUnaryExpression(); |
||||
if (peekToken(TokenKind.POWER)) { |
||||
Token t = nextToken();//consume POWER
|
||||
SpelNodeImpl expr2 = eatUnaryExpression(); |
||||
checkRightOperand(t,expr2); |
||||
return new OperatorPower(toPos(t),expr, expr2); |
||||
} |
||||
return expr; |
||||
} |
||||
|
||||
//unaryExpression: (PLUS^ | MINUS^ | BANG^) unaryExpression | primaryExpression ;
|
||||
private SpelNodeImpl eatUnaryExpression() { |
||||
if (peekToken(TokenKind.PLUS) || peekToken(TokenKind.MINUS) || peekToken(TokenKind.BANG)) { |
||||
Token t = nextToken(); |
||||
SpelNodeImpl expr = eatUnaryExpression(); |
||||
if (t.kind==TokenKind.BANG) { |
||||
return new OperatorNot(toPos(t),expr); |
||||
} else if (t.kind==TokenKind.PLUS) { |
||||
return new OperatorPlus(toPos(t),expr); |
||||
} else { |
||||
assert t.kind==TokenKind.MINUS; |
||||
return new OperatorMinus(toPos(t),expr); |
||||
} |
||||
} else { |
||||
return eatPrimaryExpression(); |
||||
} |
||||
} |
||||
|
||||
//primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?);
|
||||
private SpelNodeImpl eatPrimaryExpression() { |
||||
List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>(); |
||||
SpelNodeImpl start = eatStartNode(); |
||||
nodes.add(start); |
||||
while (maybeEatNode()) { |
||||
nodes.add(pop()); |
||||
} |
||||
if (nodes.size()==1) { |
||||
return nodes.get(0); |
||||
} else { |
||||
return new CompoundExpression(toPos(start.getStartPosition(),nodes.get(nodes.size()-1).getEndPosition()),nodes.toArray(new SpelNodeImpl[nodes.size()])); |
||||
} |
||||
} |
||||
|
||||
private int toPos(int start,int end) { |
||||
return (start<<16)+end; |
||||
} |
||||
|
||||
//node : ((DOT dottedNode) | nonDottedNode)+;
|
||||
private boolean maybeEatNode() { |
||||
Token t = peekToken(); |
||||
SpelNodeImpl expr = null; |
||||
if (t!=null && peekToken(TokenKind.DOT,TokenKind.SAFE_NAVI)) { |
||||
expr = eatDottedNode(); |
||||
} else { |
||||
expr = maybeEatNonDottedNode(); |
||||
} |
||||
if (expr==null) { |
||||
return false; |
||||
} else { |
||||
push(expr); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
//nonDottedNode: indexer;
|
||||
private SpelNodeImpl maybeEatNonDottedNode() { |
||||
if (peekToken(TokenKind.LSQUARE)) { |
||||
if (maybeEatIndexer()) { |
||||
return pop(); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
//dottedNode
|
||||
// : ((methodOrProperty
|
||||
// | functionOrVar
|
||||
// | projection
|
||||
// | selection
|
||||
// | firstSelection
|
||||
// | lastSelection
|
||||
// ))
|
||||
// ;
|
||||
private SpelNodeImpl eatDottedNode() { |
||||
Token t = nextToken();// it was a '.' or a '?.'
|
||||
//eatToken(TokenKind.DOT);
|
||||
boolean nullSafeNavigation = t.kind==TokenKind.SAFE_NAVI; |
||||
if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() || maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) { |
||||
return pop(); |
||||
} |
||||
throw new InternalParseException(new SpelParseException(expressionString,t.startpos,SpelMessages.UNEXPECTED_DATA_AFTER_DOT,toString(peekToken()))); |
||||
} |
||||
|
||||
// functionOrVar
|
||||
// : (POUND ID LPAREN) => function
|
||||
// | var
|
||||
// ;
|
||||
//
|
||||
//function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs);
|
||||
//
|
||||
//var : POUND id=ID -> ^(VARIABLEREF[$id]);
|
||||
|
||||
private boolean maybeEatFunctionOrVar() { |
||||
if (!peekToken(TokenKind.HASH)) { |
||||
return false; |
||||
} |
||||
Token t = nextToken(); |
||||
Token functionOrVariableName = eatToken(TokenKind.IDENTIFIER); |
||||
SpelNodeImpl[] args = maybeEatMethodArgs(); |
||||
if (args==null) { |
||||
push(new VariableReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos))); |
||||
return true; |
||||
} else { |
||||
push(new FunctionReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos),args)); |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
|
||||
//methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!;
|
||||
private SpelNodeImpl[] maybeEatMethodArgs() { |
||||
if (!peekToken(TokenKind.LPAREN)) { |
||||
return null; |
||||
} |
||||
List<SpelNodeImpl> args = new ArrayList<SpelNodeImpl>(); |
||||
consumeArguments(args); |
||||
eatToken(TokenKind.RPAREN); |
||||
return args.toArray(new SpelNodeImpl[args.size()]); |
||||
} |
||||
|
||||
private void eatConstructorArgs(List<SpelNodeImpl> accumulatedArguments) { |
||||
if (!peekToken(TokenKind.LPAREN)) { |
||||
throw new InternalParseException(new SpelParseException(expressionString,positionOf(peekToken()),SpelMessages.MISSING_CONSTRUCTOR_ARGS)); |
||||
} |
||||
consumeArguments(accumulatedArguments); |
||||
eatToken(TokenKind.RPAREN); |
||||
} |
||||
|
||||
/** |
||||
* Used for consuming arguments for either a method or a constructor call |
||||
*/ |
||||
private void consumeArguments(List<SpelNodeImpl> accumulatedArguments) { |
||||
int pos = peekToken().startpos; |
||||
Token next = null; |
||||
do { |
||||
nextToken();// consume ( (first time through) or comma (subsequent times)
|
||||
Token t = peekToken(); |
||||
if (t==null) { |
||||
raiseInternalException(pos,SpelMessages.RUN_OUT_OF_ARGUMENTS); |
||||
} |
||||
if (t.kind!=TokenKind.RPAREN) { |
||||
accumulatedArguments.add(eatExpression()); |
||||
} |
||||
next = peekToken(); |
||||
} while (next!=null && next.kind==TokenKind.COMMA); |
||||
if (next==null) { |
||||
raiseInternalException(pos,SpelMessages.RUN_OUT_OF_ARGUMENTS); |
||||
} |
||||
} |
||||
|
||||
private int positionOf(Token t) { |
||||
if (t==null) { |
||||
// if null assume the problem is because the right token was
|
||||
// not found at the end of the expression
|
||||
return expressionString.length(); |
||||
} else { |
||||
return t.startpos; |
||||
} |
||||
} |
||||
|
||||
|
||||
//startNode
|
||||
// : parenExpr | literal
|
||||
// | type
|
||||
// | methodOrProperty
|
||||
// | functionOrVar
|
||||
// | projection
|
||||
// | selection
|
||||
// | firstSelection
|
||||
// | lastSelection
|
||||
// | indexer
|
||||
// | constructor
|
||||
private SpelNodeImpl eatStartNode() { |
||||
if (maybeEatLiteral()) { |
||||
return pop(); |
||||
} else if (maybeEatParenExpression()) { |
||||
return pop(); |
||||
} else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { |
||||
return pop(); |
||||
} else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { |
||||
return pop(); |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
private boolean maybeEatTypeReference() { |
||||
if (peekToken(TokenKind.IDENTIFIER)) { |
||||
Token typeName = peekToken(); |
||||
if (!typeName.stringValue().equals("T")) { |
||||
return false; |
||||
} |
||||
nextToken(); |
||||
eatToken(TokenKind.LPAREN); |
||||
SpelNodeImpl node = eatPossiblyQualifiedId(); |
||||
// dotted qualified id
|
||||
eatToken(TokenKind.RPAREN); |
||||
constructedNodes.push(new TypeReference(toPos(typeName),node)); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private boolean maybeEatNullReference() { |
||||
if (peekToken(TokenKind.IDENTIFIER)) { |
||||
Token nullToken = peekToken(); |
||||
if (!nullToken.stringValue().equals("null")) { |
||||
return false; |
||||
} |
||||
nextToken(); |
||||
constructedNodes.push(new NullLiteral(toPos(nullToken))); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
//projection: PROJECT^ expression RCURLY!;
|
||||
private boolean maybeEatProjection(boolean nullSafeNavigation) { |
||||
Token t = peekToken(); |
||||
if (!peekToken(TokenKind.PROJECT)) { |
||||
return false; |
||||
} |
||||
nextToken(); |
||||
SpelNodeImpl expr = eatExpression(); |
||||
eatToken(TokenKind.RSQUARE); |
||||
constructedNodes.push(new Projection(nullSafeNavigation, toPos(t),expr)); |
||||
return true; |
||||
} |
||||
|
||||
private boolean maybeEatIndexer() { |
||||
Token t = peekToken(); |
||||
if (!peekToken(TokenKind.LSQUARE)) { |
||||
return false; |
||||
} |
||||
nextToken(); |
||||
SpelNodeImpl expr = eatExpression(); |
||||
eatToken(TokenKind.RSQUARE); |
||||
constructedNodes.push(new Indexer(toPos(t),expr)); |
||||
return true; |
||||
} |
||||
|
||||
private boolean maybeEatSelection(boolean nullSafeNavigation) { |
||||
Token t = peekToken(); |
||||
if (!peekSelectToken()) { |
||||
return false; |
||||
} |
||||
nextToken(); |
||||
SpelNodeImpl expr = eatExpression(); |
||||
eatToken(TokenKind.RSQUARE); |
||||
if (t.kind==TokenKind.SELECT_FIRST) { |
||||
constructedNodes.push(new Selection(nullSafeNavigation,Selection.FIRST,toPos(t),expr)); |
||||
} else if (t.kind==TokenKind.SELECT_LAST) { |
||||
constructedNodes.push(new Selection(nullSafeNavigation,Selection.LAST,toPos(t),expr)); |
||||
} else { |
||||
constructedNodes.push(new Selection(nullSafeNavigation,Selection.ALL,toPos(t),expr)); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private SpelNodeImpl eatPossiblyQualifiedId() { |
||||
List<SpelNodeImpl> qualifiedIdPieces = new ArrayList<SpelNodeImpl>(); |
||||
Token startnode = eatToken(TokenKind.IDENTIFIER); |
||||
qualifiedIdPieces.add(new Identifier(startnode.stringValue(),toPos(startnode))); |
||||
while (peekToken(TokenKind.DOT)) { |
||||
nextToken(); |
||||
Token node = eatToken(TokenKind.IDENTIFIER); |
||||
qualifiedIdPieces.add(new Identifier(node.stringValue(),toPos(node))); |
||||
} |
||||
return new QualifiedIdentifier(toPos(startnode.startpos,qualifiedIdPieces.get(qualifiedIdPieces.size()-1).getEndPosition()),qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()])); |
||||
} |
||||
|
||||
private boolean maybeEatMethodOrProperty(boolean nullSafeNavigation) { |
||||
if (peekToken(TokenKind.IDENTIFIER)) { |
||||
Token methodOrPropertyName = nextToken(); |
||||
SpelNodeImpl[] args = maybeEatMethodArgs(); |
||||
if (args==null) { |
||||
// property
|
||||
push(new PropertyOrFieldReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName))); |
||||
return true; |
||||
} else { |
||||
// methodreference
|
||||
push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args)); |
||||
// TODO what is the end position for a method reference? the name or the last arg?
|
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
//constructor
|
||||
// : ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs)
|
||||
// ;
|
||||
private boolean maybeEatConstructorReference() { |
||||
if (peekIdentifierToken("new")) { |
||||
Token newToken = nextToken(); |
||||
SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); |
||||
List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>(); |
||||
nodes.add(possiblyQualifiedConstructorName); |
||||
eatConstructorArgs(nodes); |
||||
push(new ConstructorReference(toPos(newToken),nodes.toArray(new SpelNodeImpl[nodes.size()]))); // TODO correct end position?
|
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
|
||||
private void push(SpelNodeImpl newNode) { |
||||
constructedNodes.push(newNode); |
||||
} |
||||
|
||||
private SpelNodeImpl pop() { |
||||
return constructedNodes.pop(); |
||||
} |
||||
|
||||
// literal:
|
||||
// INTEGER_LITERAL
|
||||
// | boolLiteral
|
||||
// | STRING_LITERAL
|
||||
|
||||
// | HEXADECIMAL_INTEGER_LITERAL
|
||||
// | REAL_LITERAL
|
||||
// | DQ_STRING_LITERAL
|
||||
// | NULL_LITERAL
|
||||
// ;
|
||||
private boolean maybeEatLiteral() { |
||||
Token t = peekToken(); |
||||
if (t==null) { |
||||
return false; |
||||
} |
||||
if (t.kind==TokenKind.LITERAL_INT) { |
||||
nextToken(); |
||||
push(Literal.getIntLiteral(t.data, toPos(t), 10)); |
||||
return true; |
||||
} else if (t.kind==TokenKind.LITERAL_LONG) { |
||||
nextToken(); |
||||
push(Literal.getLongLiteral(t.data, toPos(t), 10)); |
||||
return true; |
||||
} else if (t.kind==TokenKind.LITERAL_HEXINT) { |
||||
nextToken(); |
||||
push(Literal.getIntLiteral(t.data, toPos(t), 16)); |
||||
return true; |
||||
} else if (t.kind==TokenKind.LITERAL_HEXLONG) { |
||||
nextToken(); |
||||
push(Literal.getLongLiteral(t.data, toPos(t), 16)); |
||||
return true; |
||||
} else if (t.kind==TokenKind.LITERAL_REAL) { |
||||
nextToken(); |
||||
push(Literal.getRealLiteral(t.data, toPos(t),false)); |
||||
return true; |
||||
} else if (t.kind==TokenKind.LITERAL_REAL_FLOAT) { |
||||
nextToken(); |
||||
push(Literal.getRealLiteral(t.data, toPos(t), true)); |
||||
return true; |
||||
} else if (peekIdentifierToken("true")) { |
||||
nextToken(); |
||||
push(new BooleanLiteral(t.data,toPos(t),true)); |
||||
return true; |
||||
} else if (peekIdentifierToken("false")) { |
||||
nextToken(); |
||||
push(new BooleanLiteral(t.data,toPos(t),false)); |
||||
return true; |
||||
} else if (t.kind==TokenKind.LITERAL_STRING) { |
||||
nextToken(); |
||||
push(new StringLiteral(t.data,toPos(t),t.data)); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private int toPos(Token t) { |
||||
return (t.startpos<<16)+t.endpos; |
||||
} |
||||
|
||||
//parenExpr : LPAREN! expression RPAREN!;
|
||||
private boolean maybeEatParenExpression() { |
||||
if (peekToken(TokenKind.LPAREN)) { |
||||
nextToken(); |
||||
SpelNodeImpl expr = eatExpression(); |
||||
eatToken(TokenKind.RPAREN); |
||||
push(expr); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
|
||||
// relationalOperator
|
||||
// : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN | GREATER_THAN_OR_EQUAL | INSTANCEOF
|
||||
// | BETWEEN | MATCHES
|
||||
private Token maybeEatRelationalOperator() { |
||||
Token t = peekToken(); |
||||
if (t==null) { |
||||
return null; |
||||
} |
||||
if (t.isNumericRelationalOperator()) { |
||||
return t; |
||||
} |
||||
if (t.isIdentifier()) { |
||||
String idString = t.stringValue(); |
||||
if (idString.equalsIgnoreCase("instanceof")) { |
||||
return t.asInstanceOfToken(); |
||||
} else if (idString.equalsIgnoreCase("matches")) { |
||||
return t.asMatchesToken(); |
||||
} else if (idString.equalsIgnoreCase("between")) { |
||||
return t.asBetweenToken(); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private Token eatToken(TokenKind expectedKind) { |
||||
assert moreTokens(); |
||||
Token t = nextToken(); |
||||
if (t==null) { |
||||
raiseInternalException( expressionString.length(), SpelMessages.OOD); |
||||
} |
||||
if (t.kind!=expectedKind) { |
||||
raiseInternalException(t.startpos,SpelMessages.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),t.getKind().toString().toLowerCase()); |
||||
} |
||||
return t; |
||||
} |
||||
|
||||
private boolean peekToken(TokenKind desiredTokenKind) { |
||||
if (!moreTokens()) return false; |
||||
Token t = peekToken(); |
||||
return t.kind==desiredTokenKind; |
||||
} |
||||
|
||||
private boolean peekToken(TokenKind possible1,TokenKind possible2) { |
||||
if (!moreTokens()) return false; |
||||
Token t = peekToken(); |
||||
return t.kind==possible1 || t.kind==possible2; |
||||
} |
||||
|
||||
private boolean peekToken(TokenKind possible1,TokenKind possible2, TokenKind possible3) { |
||||
if (!moreTokens()) return false; |
||||
Token t = peekToken(); |
||||
return t.kind==possible1 || t.kind==possible2 || t.kind==possible3; |
||||
} |
||||
|
||||
private boolean peekIdentifierToken(String identifierString) { |
||||
if (!moreTokens()) return false; |
||||
Token t = peekToken(); |
||||
return t.kind==TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString); |
||||
} |
||||
|
||||
private boolean peekSelectToken() { |
||||
if (!moreTokens()) return false; |
||||
Token t = peekToken(); |
||||
return t.kind==TokenKind.SELECT || t.kind==TokenKind.SELECT_FIRST || t.kind==TokenKind.SELECT_LAST; |
||||
} |
||||
|
||||
|
||||
private boolean moreTokens() { |
||||
return tokenStreamPointer<tokenStream.size(); |
||||
} |
||||
|
||||
|
||||
private Token nextToken() { |
||||
if (tokenStreamPointer>=tokenStreamLength) { |
||||
return null; |
||||
} |
||||
return tokenStream.get(tokenStreamPointer++); |
||||
} |
||||
|
||||
private Token peekToken() { |
||||
if (tokenStreamPointer>=tokenStreamLength) { |
||||
return null; |
||||
} |
||||
return tokenStream.get(tokenStreamPointer); |
||||
} |
||||
|
||||
private void raiseInternalException(int pos, SpelMessages message,Object... inserts) { |
||||
throw new InternalParseException(new SpelParseException(expressionString,pos,message,inserts)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
/* |
||||
* Copyright 2002-2009 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.standard; |
||||
|
||||
/** |
||||
* Holder for a kind of token, the associated data and its position in the input data stream (start/end). |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
class Token { |
||||
|
||||
TokenKind kind; |
||||
String data; |
||||
int startpos; // index of first character
|
||||
int endpos; // index of char after the last character
|
||||
|
||||
/** |
||||
* Constructor for use when there is no particular data for the token (eg. TRUE or '+') |
||||
* @param startpos the exact start |
||||
* @param endpos the index to the last character |
||||
*/ |
||||
Token(TokenKind tokenKind, int startpos, int endpos) { |
||||
this.kind = tokenKind; |
||||
this.startpos = startpos; |
||||
this.endpos = endpos; |
||||
} |
||||
|
||||
Token(TokenKind tokenKind, char[] tokenData, int pos, int endpos) { |
||||
this(tokenKind,pos,endpos); |
||||
this.data = new String(tokenData); |
||||
} |
||||
|
||||
|
||||
public TokenKind getKind() { |
||||
return kind; |
||||
} |
||||
|
||||
public String toString() { |
||||
StringBuilder s = new StringBuilder(); |
||||
s.append("[").append(kind.toString()); |
||||
if (kind.hasPayload()) { |
||||
s.append(":").append(data); |
||||
} |
||||
s.append("]"); |
||||
s.append("(").append(startpos).append(",").append(endpos).append(")"); |
||||
return s.toString(); |
||||
} |
||||
|
||||
public boolean isIdentifier() { |
||||
return kind==TokenKind.IDENTIFIER; |
||||
} |
||||
|
||||
public boolean isGreaterThan() { |
||||
return kind==TokenKind.GT; |
||||
} |
||||
|
||||
public boolean isLessThan() { |
||||
return kind==TokenKind.LT; |
||||
} |
||||
|
||||
public boolean isGreaterThanOrEqual() { |
||||
return kind==TokenKind.GE; |
||||
} |
||||
|
||||
public boolean isEquality() { |
||||
return kind==TokenKind.EQ; |
||||
} |
||||
|
||||
public boolean isLessThanOrEqual() { |
||||
return kind==TokenKind.LE; |
||||
} |
||||
|
||||
public boolean isInstanceOf() { |
||||
return kind==TokenKind.INSTANCEOF; |
||||
} |
||||
|
||||
public boolean isNumericRelationalOperator() { |
||||
return kind==TokenKind.GT || kind==TokenKind.GE || kind==TokenKind.LT || kind==TokenKind.LE || kind==TokenKind.EQ || kind==TokenKind.NE; |
||||
} |
||||
|
||||
public String stringValue() { |
||||
return data; |
||||
} |
||||
|
||||
public Token asInstanceOfToken() { |
||||
return new Token(TokenKind.INSTANCEOF,startpos,endpos); |
||||
} |
||||
|
||||
public Token asMatchesToken() { |
||||
return new Token(TokenKind.MATCHES,startpos,endpos); |
||||
} |
||||
|
||||
public Token asBetweenToken() { |
||||
return new Token(TokenKind.BETWEEN,startpos,endpos); |
||||
} |
||||
} |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
/* |
||||
* Copyright 2002-2009 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.standard; |
||||
|
||||
/** |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
enum TokenKind { |
||||
// ordered by priority - operands first
|
||||
LITERAL_INT, LITERAL_LONG, LITERAL_HEXINT, LITERAL_HEXLONG, LITERAL_STRING, LITERAL_REAL, LITERAL_REAL_FLOAT, |
||||
LPAREN("("), RPAREN(")"), COMMA(","), IDENTIFIER, |
||||
COLON(":"),HASH("#"),RSQUARE("]"), LSQUARE("["), |
||||
DOT("."), PLUS("+"), STAR("*"), DIV("/"), BANG("!"), MINUS("-"), SELECT_FIRST("^["), SELECT_LAST("$["), QMARK("?"), PROJECT("!["), |
||||
GE(">="),GT(">"),LE("<="),LT("<"),EQ("=="),NE("!="),ASSIGN("="), INSTANCEOF("instanceof"), MATCHES("matches"), BETWEEN("between"), |
||||
SELECT("?["), MOD("%"), POWER("^"), DOLLAR("$"), |
||||
ELVIS("?:"), SAFE_NAVI("?."); |
||||
; |
||||
|
||||
char[] tokenChars; |
||||
private boolean hasPayload; // is there more to this token than simply the kind
|
||||
|
||||
private TokenKind(String tokenString) { |
||||
tokenChars = tokenString.toCharArray(); |
||||
hasPayload = tokenChars.length==0; |
||||
} |
||||
|
||||
private TokenKind() { |
||||
this(""); |
||||
} |
||||
|
||||
public String toString() { |
||||
return this.name()+(tokenChars.length!=0?"("+new String(tokenChars)+")":""); |
||||
} |
||||
|
||||
public boolean hasPayload() { |
||||
return hasPayload; |
||||
} |
||||
} |
||||
@ -0,0 +1,474 @@
@@ -0,0 +1,474 @@
|
||||
/* |
||||
* Copyright 2002-2009 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.standard; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.springframework.expression.spel.SpelMessages; |
||||
import org.springframework.expression.spel.SpelParseException; |
||||
|
||||
/** |
||||
* Lex some input data into a stream of tokens that can then be parsed. |
||||
* |
||||
* @author Andy Clement |
||||
* @since 3.0 |
||||
*/ |
||||
public class Tokenizer { |
||||
|
||||
String expressionString; |
||||
char[] toProcess; |
||||
int pos; |
||||
int max; |
||||
List<Token> tokens = new ArrayList<Token>(); |
||||
|
||||
public Tokenizer(String inputdata) { |
||||
this.expressionString = inputdata; |
||||
this.toProcess = (inputdata+"\0").toCharArray(); |
||||
this.max = toProcess.length; |
||||
this.pos = 0; |
||||
process(); |
||||
} |
||||
|
||||
public void process() { |
||||
while (pos<max) { |
||||
char ch = toProcess[pos]; |
||||
if (isAlphabetic(ch)) { |
||||
lexIdentifier(); |
||||
} else { |
||||
switch (ch) { |
||||
case '+': |
||||
pushCharToken(TokenKind.PLUS); |
||||
break; |
||||
case '-': |
||||
pushCharToken(TokenKind.MINUS); |
||||
break; |
||||
case ':': |
||||
pushCharToken(TokenKind.COLON); |
||||
break; |
||||
case '.': |
||||
pushCharToken(TokenKind.DOT); |
||||
break; |
||||
case ',': |
||||
pushCharToken(TokenKind.COMMA); |
||||
break; |
||||
case '*': |
||||
pushCharToken(TokenKind.STAR); |
||||
break; |
||||
case '/': |
||||
pushCharToken(TokenKind.DIV); |
||||
break; |
||||
case '%': |
||||
pushCharToken(TokenKind.MOD); |
||||
break; |
||||
case '(': |
||||
pushCharToken(TokenKind.LPAREN); |
||||
break; |
||||
case ')': |
||||
pushCharToken(TokenKind.RPAREN); |
||||
break; |
||||
case '[': |
||||
pushCharToken(TokenKind.LSQUARE); |
||||
break; |
||||
case '#': |
||||
pushCharToken(TokenKind.HASH); |
||||
break; |
||||
case ']': |
||||
pushCharToken(TokenKind.RSQUARE); |
||||
break; |
||||
case '^': |
||||
if (isTwoCharToken(TokenKind.SELECT_FIRST)) { |
||||
pushPairToken(TokenKind.SELECT_FIRST); |
||||
} else { |
||||
pushCharToken(TokenKind.POWER); |
||||
} |
||||
break; |
||||
case '!': |
||||
if (isTwoCharToken(TokenKind.NE)) { |
||||
pushPairToken(TokenKind.NE); |
||||
} else if (isTwoCharToken(TokenKind.PROJECT)) { |
||||
pushPairToken(TokenKind.PROJECT); |
||||
} else { |
||||
pushCharToken(TokenKind.BANG); |
||||
} |
||||
break; |
||||
case '=': |
||||
if (isTwoCharToken(TokenKind.EQ)) { |
||||
pushPairToken(TokenKind.EQ); |
||||
} else { |
||||
pushCharToken(TokenKind.ASSIGN); |
||||
} |
||||
break; |
||||
case '?': |
||||
if (isTwoCharToken(TokenKind.SELECT)) { |
||||
pushPairToken(TokenKind.SELECT); |
||||
} else if (isTwoCharToken(TokenKind.ELVIS)) { |
||||
pushPairToken(TokenKind.ELVIS); |
||||
} else if (isTwoCharToken(TokenKind.SAFE_NAVI)) { |
||||
pushPairToken(TokenKind.SAFE_NAVI); |
||||
} else { |
||||
pushCharToken(TokenKind.QMARK); |
||||
} |
||||
break; |
||||
case '$': |
||||
if (isTwoCharToken(TokenKind.SELECT_LAST)) { |
||||
pushPairToken(TokenKind.SELECT_LAST); |
||||
} else { |
||||
pushCharToken(TokenKind.DOLLAR); |
||||
} |
||||
break; |
||||
case '>': |
||||
if (isTwoCharToken(TokenKind.GE)) { |
||||
pushPairToken(TokenKind.GE); |
||||
} else { |
||||
pushCharToken(TokenKind.GT); |
||||
} |
||||
break; |
||||
case '<': |
||||
if (isTwoCharToken(TokenKind.LE)) { |
||||
pushPairToken(TokenKind.LE); |
||||
} else { |
||||
pushCharToken(TokenKind.LT); |
||||
} |
||||
break; |
||||
case '0': |
||||
case '1': |
||||
case '2': |
||||
case '3': |
||||
case '4': |
||||
case '5': |
||||
case '6': |
||||
case '7': |
||||
case '8': |
||||
case '9': |
||||
lexNumericLiteral(ch=='0'); |
||||
break; |
||||
case ' ': |
||||
case '\t': |
||||
case '\r': |
||||
case '\n': |
||||
// drift over white space
|
||||
pos++; |
||||
break; |
||||
case '\'': |
||||
lexQuotedStringLiteral(); |
||||
break; |
||||
case '"': |
||||
lexDoubleQuotedStringLiteral(); |
||||
break; |
||||
case 0: |
||||
// hit sentinel at end of value
|
||||
pos++; // will take us to the end
|
||||
break; |
||||
default: |
||||
throw new IllegalStateException("Cannot handle ("+Integer.valueOf(ch)+") '"+ch+"'"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public List<Token> getTokens() { |
||||
return tokens; |
||||
} |
||||
|
||||
|
||||
// STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
|
||||
private void lexQuotedStringLiteral() { |
||||
int start = pos; |
||||
boolean terminated = false; |
||||
while (!terminated) { |
||||
pos++; |
||||
char ch = toProcess[pos]; |
||||
if (ch=='\'') { |
||||
// may not be the end if the char after is also a '
|
||||
if (toProcess[pos+1]=='\'') { |
||||
pos++; // skip over that too, and continue
|
||||
} else { |
||||
terminated = true; |
||||
} |
||||
} |
||||
if (ch==0) { |
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessages.NON_TERMINATING_QUOTED_STRING)); |
||||
} |
||||
} |
||||
pos++; |
||||
tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, pos)); |
||||
} |
||||
|
||||
// DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
|
||||
private void lexDoubleQuotedStringLiteral() { |
||||
int start = pos; |
||||
boolean terminated = false; |
||||
while (!terminated) { |
||||
pos++; |
||||
char ch = toProcess[pos]; |
||||
if (ch=='"') { |
||||
terminated = true; |
||||
} |
||||
if (ch==0) { |
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessages.NON_TERMINATING_DOUBLE_QUOTED_STRING)); |
||||
} |
||||
} |
||||
pos++; |
||||
tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, pos)); |
||||
} |
||||
|
||||
|
||||
// REAL_LITERAL :
|
||||
// ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
|
||||
// ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
|
||||
// ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
|
||||
// ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
|
||||
// fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
|
||||
// fragment HEX_DIGIT : '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';
|
||||
//
|
||||
// fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* (DECIMAL_DIGIT)+ ;
|
||||
// fragment SIGN : '+' | '-' ;
|
||||
// fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';
|
||||
// INTEGER_LITERAL
|
||||
// : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
|
||||
|
||||
private void lexNumericLiteral(boolean firstCharIsZero) { |
||||
boolean isReal = false; |
||||
int start = pos; |
||||
char ch = toProcess[pos+1]; |
||||
boolean isHex = ch=='x' || ch=='X'; |
||||
|
||||
// deal with hexadecimal
|
||||
if (firstCharIsZero && isHex) { |
||||
pos=pos+1; |
||||
do { |
||||
pos++; |
||||
} while (isHexadecimalDigit(toProcess[pos])); |
||||
if (isChar('L','l')) { |
||||
pushHexIntToken(subarray(start+2,pos),true, start, pos); |
||||
pos++; |
||||
} else { |
||||
pushHexIntToken(subarray(start+2,pos),false, start, pos); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
// real numbers must have leading digits
|
||||
|
||||
// Consume first part of number
|
||||
do { |
||||
pos++; |
||||
} while (isDigit(toProcess[pos])); |
||||
|
||||
// a '.' indicates this number is a real
|
||||
ch = toProcess[pos]; |
||||
if (ch=='.') { |
||||
isReal = true; |
||||
// carry on consuming digits
|
||||
do { |
||||
pos++; |
||||
} while (isDigit(toProcess[pos])); |
||||
} |
||||
|
||||
int endOfNumber = pos; |
||||
|
||||
// Now there may or may not be an exponent
|
||||
|
||||
// is it a long ?
|
||||
if (isChar('L','l')) { |
||||
if (isReal) { // 3.4L - not allowed
|
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessages.REAL_CANNOT_BE_LONG)); |
||||
} |
||||
pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber); |
||||
pos++; |
||||
} else if (isExponentChar(toProcess[pos])) { |
||||
isReal = true; // if it wasnt before, it is now
|
||||
pos++; |
||||
char possibleSign = toProcess[pos]; |
||||
if (isSign(possibleSign)) { |
||||
pos++; |
||||
} |
||||
|
||||
// exponent digits
|
||||
do { |
||||
pos++; |
||||
} while (isDigit(toProcess[pos])); |
||||
boolean isFloat = false; |
||||
if (isFloatSuffix(toProcess[pos])) { |
||||
isFloat = true; |
||||
endOfNumber = ++pos; |
||||
} else if (isDoubleSuffix(toProcess[pos])) { |
||||
endOfNumber = ++pos; |
||||
} |
||||
pushRealToken(subarray(start,pos), isFloat, start, pos); |
||||
} else { |
||||
ch = toProcess[pos]; |
||||
boolean isFloat = false; |
||||
if (isFloatSuffix(ch)) { |
||||
isReal = true; |
||||
isFloat = true; |
||||
endOfNumber = ++pos; |
||||
} else if (isDoubleSuffix(ch)) { |
||||
isReal = true; |
||||
endOfNumber = ++pos; |
||||
} |
||||
if (isReal) { |
||||
pushRealToken(subarray(start,endOfNumber), isFloat, start, endOfNumber); |
||||
} else { |
||||
pushIntToken(subarray(start,endOfNumber), false, start, endOfNumber); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
private void lexIdentifier() { |
||||
int start = pos; |
||||
do { |
||||
pos++; |
||||
} while (isIdentifier(toProcess[pos])); |
||||
tokens.add(new Token(TokenKind.IDENTIFIER,subarray(start,pos),start,pos)); |
||||
} |
||||
|
||||
private void pushIntToken(char[] data,boolean isLong, int start, int end) { |
||||
if (isLong) { |
||||
tokens.add(new Token(TokenKind.LITERAL_LONG,data, start, end)); |
||||
} else { |
||||
tokens.add(new Token(TokenKind.LITERAL_INT,data, start, end)); |
||||
} |
||||
} |
||||
|
||||
private void pushHexIntToken(char[] data,boolean isLong, int start, int end) { |
||||
if (data.length==0) { |
||||
if (isLong) { |
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessages.NOT_A_LONG,expressionString.substring(start,end+1))); |
||||
} else { |
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessages.NOT_AN_INTEGER,expressionString.substring(start,end))); |
||||
} |
||||
} |
||||
if (isLong) { |
||||
tokens.add(new Token(TokenKind.LITERAL_HEXLONG, data, start, end)); |
||||
} else { |
||||
tokens.add(new Token(TokenKind.LITERAL_HEXINT, data, start, end)); |
||||
} |
||||
} |
||||
|
||||
private void pushRealToken(char[] data, boolean isFloat, int start, int end) { |
||||
if (isFloat) { |
||||
tokens.add(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end)); |
||||
} else { |
||||
tokens.add(new Token(TokenKind.LITERAL_REAL, data, start, end)); |
||||
} |
||||
} |
||||
|
||||
private char[] subarray(int start, int end) { |
||||
char[] result = new char[end - start]; |
||||
System.arraycopy(toProcess, start, result, 0, end - start); |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Check if this might be a two character token. |
||||
*/ |
||||
private boolean isTwoCharToken(TokenKind kind) { |
||||
assert kind.tokenChars.length==2; |
||||
assert toProcess[pos] == kind.tokenChars[0]; |
||||
return toProcess[pos+1] == kind.tokenChars[1]; |
||||
} |
||||
|
||||
/** |
||||
* Push a token of just one character in length. |
||||
*/ |
||||
private void pushCharToken(TokenKind kind) { |
||||
tokens.add(new Token(kind,pos,pos+1)); |
||||
pos++; |
||||
} |
||||
|
||||
/** |
||||
* Push a token of two characters in length. |
||||
*/ |
||||
private void pushPairToken(TokenKind kind) { |
||||
tokens.add(new Token(kind,pos,pos+2)); |
||||
pos+=2; |
||||
} |
||||
|
||||
// ID: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*;
|
||||
private boolean isIdentifier(char ch) { |
||||
return isAlphabetic(ch) || isDigit(ch) || ch=='_'; |
||||
} |
||||
|
||||
private boolean isChar(char a,char b) { |
||||
char ch = toProcess[pos]; |
||||
return ch==a || ch==b; |
||||
} |
||||
|
||||
private boolean isExponentChar(char ch) { |
||||
return ch=='e' || ch=='E'; |
||||
} |
||||
|
||||
private boolean isFloatSuffix(char ch) { |
||||
return ch=='f' || ch=='F'; |
||||
} |
||||
|
||||
private boolean isDoubleSuffix(char ch) { |
||||
return ch=='d' || ch=='D'; |
||||
} |
||||
|
||||
private boolean isSign(char ch) { |
||||
return ch=='+' || ch=='-'; |
||||
} |
||||
|
||||
private boolean isDigit(char ch) { |
||||
if (ch>255) { |
||||
return false; |
||||
} |
||||
return (flags[ch] & IS_DIGIT)!=0; |
||||
} |
||||
|
||||
private boolean isAlphabetic(char ch) { |
||||
if (ch>255) { |
||||
return false; |
||||
} |
||||
return (flags[ch] & IS_ALPHA)!=0; |
||||
} |
||||
|
||||
private boolean isHexadecimalDigit(char ch) { |
||||
if (ch>255) { |
||||
return false; |
||||
} |
||||
return (flags[ch] & IS_HEXDIGIT)!=0; |
||||
} |
||||
|
||||
private static final byte flags[] = new byte[256]; |
||||
private static final byte IS_DIGIT=0x01; |
||||
private static final byte IS_HEXDIGIT=0x02; |
||||
private static final byte IS_ALPHA=0x04; |
||||
|
||||
static { |
||||
for (int ch='0';ch<='9';ch++) { |
||||
flags[ch]|=IS_DIGIT | IS_HEXDIGIT; |
||||
} |
||||
for (int ch='A';ch<='F';ch++) { |
||||
flags[ch]|= IS_HEXDIGIT; |
||||
} |
||||
for (int ch='a';ch<='f';ch++) { |
||||
flags[ch]|= IS_HEXDIGIT; |
||||
} |
||||
for (int ch='A';ch<='Z';ch++) { |
||||
flags[ch]|= IS_ALPHA; |
||||
} |
||||
for (int ch='a';ch<='z';ch++) { |
||||
flags[ch]|= IS_ALPHA; |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue