First drop of SPEL
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.OperatorOverloader;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypeComparator;
|
||||
import org.springframework.expression.TypeUtils;
|
||||
import org.springframework.expression.spel.internal.VariableScope;
|
||||
|
||||
/**
|
||||
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other
|
||||
* expressions but it gives a place to hold local variables and for component expressions in a compound expression to
|
||||
* communicate state. This is in contrast to the EvaluationContext, which is shared amongst expression evaluations, and
|
||||
* any changes to it will be seen by other expressions or any code that chooses to ask questions of the context.
|
||||
*
|
||||
* It also acts as a place for to define common utility routines that the various Ast nodes might need.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ExpressionState {
|
||||
|
||||
private EvaluationContext relatedContext;
|
||||
|
||||
private final Stack<VariableScope> environment = new Stack<VariableScope>();
|
||||
|
||||
private final Stack<Object> contextObjects = new Stack<Object>();
|
||||
|
||||
public ExpressionState(EvaluationContext context) {
|
||||
this.relatedContext = context;
|
||||
createEnvironment();
|
||||
}
|
||||
|
||||
public ExpressionState() {
|
||||
createEnvironment();
|
||||
}
|
||||
|
||||
private void createEnvironment() {
|
||||
environment.add(new VariableScope()); // create an empty top level VariableScope
|
||||
}
|
||||
|
||||
/**
|
||||
* The active context object is what unqualified references to properties/etc are resolved against.
|
||||
*/
|
||||
public Object getActiveContextObject() {
|
||||
if (contextObjects.isEmpty()) {
|
||||
return relatedContext.getRootContextObject();
|
||||
}
|
||||
return contextObjects.peek();
|
||||
}
|
||||
|
||||
public void pushActiveContextObject(Object obj) {
|
||||
contextObjects.push(obj);
|
||||
}
|
||||
|
||||
public void popActiveContextObject() {
|
||||
contextObjects.pop();
|
||||
}
|
||||
|
||||
public Object getRootContextObject() {
|
||||
return relatedContext.getRootContextObject();
|
||||
}
|
||||
|
||||
public Object lookupReference(Object contextName, Object objectName) throws EvaluationException {
|
||||
return relatedContext.lookupReference(contextName, objectName);
|
||||
}
|
||||
|
||||
public TypeUtils getTypeUtilities() {
|
||||
return relatedContext.getTypeUtils();
|
||||
}
|
||||
|
||||
public TypeComparator getTypeComparator() {
|
||||
return relatedContext.getTypeUtils().getTypeComparator();
|
||||
}
|
||||
|
||||
public Class<?> findType(String type) throws EvaluationException {
|
||||
return getTypeUtilities().getTypeLocator().findType(type);
|
||||
}
|
||||
|
||||
public boolean toBoolean(Object value) throws EvaluationException {
|
||||
// TODO cache TypeConverter when it is set/changed?
|
||||
return ((Boolean) getTypeUtilities().getTypeConverter().convertValue(value, Boolean.TYPE)).booleanValue();
|
||||
}
|
||||
|
||||
public char toCharacter(Object value) throws EvaluationException {
|
||||
return ((Character) getTypeUtilities().getTypeConverter().convertValue(value, Character.TYPE)).charValue();
|
||||
}
|
||||
|
||||
public short toShort(Object value) throws EvaluationException {
|
||||
return ((Short) getTypeUtilities().getTypeConverter().convertValue(value, Short.TYPE)).shortValue();
|
||||
}
|
||||
|
||||
public int toInteger(Object value) throws EvaluationException {
|
||||
return ((Integer) getTypeUtilities().getTypeConverter().convertValue(value, Integer.TYPE)).intValue();
|
||||
}
|
||||
|
||||
public double toDouble(Object value) throws EvaluationException {
|
||||
return ((Double) getTypeUtilities().getTypeConverter().convertValue(value, Double.TYPE)).doubleValue();
|
||||
}
|
||||
|
||||
public float toFloat(Object value) throws EvaluationException {
|
||||
return ((Float) getTypeUtilities().getTypeConverter().convertValue(value, Float.TYPE)).floatValue();
|
||||
}
|
||||
|
||||
public long toLong(Object value) throws EvaluationException {
|
||||
return ((Long) getTypeUtilities().getTypeConverter().convertValue(value, Long.TYPE)).longValue();
|
||||
}
|
||||
|
||||
public byte toByte(Object value) throws EvaluationException {
|
||||
return ((Byte) getTypeUtilities().getTypeConverter().convertValue(value, Byte.TYPE)).byteValue();
|
||||
}
|
||||
|
||||
public void setVariable(String name, Object value) {
|
||||
relatedContext.setVariable(name, value);
|
||||
}
|
||||
|
||||
public Object lookupVariable(String name) {
|
||||
return relatedContext.lookupVariable(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A new scope is entered when a function is invoked
|
||||
*/
|
||||
public void enterScope(Map<String, Object> argMap) {
|
||||
environment.push(new VariableScope(argMap));
|
||||
}
|
||||
|
||||
public void enterScope(String name, Object value) {
|
||||
environment.push(new VariableScope(name, value));
|
||||
}
|
||||
|
||||
public void exitScope() {
|
||||
environment.pop();
|
||||
}
|
||||
|
||||
public void setLocalVariable(String name, Object value) {
|
||||
environment.peek().setVariable(name, value);
|
||||
}
|
||||
|
||||
public Object lookupLocalVariable(String name) {
|
||||
int scopeNumber = environment.size() - 1;
|
||||
for (int i = scopeNumber; i >= 0; i--) {
|
||||
if (environment.get(i).definesVariable(name)) {
|
||||
return environment.get(i).lookupVariable(name);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object operate(Operation op, Object left, Object right) throws SpelException {
|
||||
OperatorOverloader overloader = relatedContext.getTypeUtils().getOperatorOverloader();
|
||||
try {
|
||||
if (overloader != null && overloader.overridesOperation(op, left, right)) {
|
||||
return overloader.operate(op, left, right);
|
||||
} else {
|
||||
throw new SpelException(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, left, right);
|
||||
}
|
||||
} catch (EvaluationException e) {
|
||||
if (e instanceof SpelException) {
|
||||
throw (SpelException) e;
|
||||
} else {
|
||||
throw new SpelException(e, SpelMessages.UNEXPECTED_PROBLEM_INVOKING_OPERATOR, op, left, right, e
|
||||
.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<PropertyAccessor> getPropertyAccessors() {
|
||||
return relatedContext.getPropertyAccessors();
|
||||
}
|
||||
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
return relatedContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
public class SpelException extends EvaluationException {
|
||||
|
||||
private SpelMessages message;
|
||||
private int position = -1;
|
||||
private Object[] inserts;
|
||||
|
||||
public SpelException(int position, Throwable cause, SpelMessages message, Object... inserts) {
|
||||
super(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;
|
||||
}
|
||||
|
||||
public SpelException(String expressionString, int position, Throwable cause, SpelMessages message, Object... inserts) {
|
||||
super(expressionString, cause);
|
||||
this.position = position;
|
||||
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 position within the expression that gave rise to the exception (or -1 if unknown)
|
||||
*/
|
||||
public int getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the unformatted message
|
||||
*/
|
||||
public SpelMessages getMessageUnformatted() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position in the related expression which gave rise to this exception.
|
||||
*
|
||||
* @param position the position in the expression that gave rise to the exception
|
||||
*/
|
||||
public void setPosition(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message inserts
|
||||
*/
|
||||
public Object[] getInserts() {
|
||||
return inserts;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ast.SpelNode;
|
||||
import org.springframework.expression.spel.standard.StandardEvaluationContext;
|
||||
|
||||
// TODO 3 Do we need more getValue() options - for example with just a root object or with a set of variables?
|
||||
// (these things are currently captured in the Context)
|
||||
|
||||
/**
|
||||
* A SpelExpressions represents a parsed (valid) expression that is ready to be evaluated in a specified context. An
|
||||
* expression can be evaluated standalone or in a specified context. During expression evaluation the context may be
|
||||
* asked to resolve references to types, beans, properties, methods.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*
|
||||
*/
|
||||
public class SpelExpression implements Expression {
|
||||
private final String expression;
|
||||
public final SpelNode ast;
|
||||
|
||||
/**
|
||||
* Construct an expression, only used by the parser.
|
||||
*
|
||||
* @param expression
|
||||
* @param ast
|
||||
*/
|
||||
SpelExpression(String expression, SpelNode ast) {
|
||||
this.expression = expression;
|
||||
this.ast = ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the expression string that was parsed to create this expression instance
|
||||
*/
|
||||
public String getExpressionString() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the expression in a default context that knows nothing (and therefore cannot resolve references to
|
||||
* properties/etc). Only useful for trivial expressions like '3+4'.
|
||||
*
|
||||
* @return the value of the expression
|
||||
* @throws SpelException if there is a problem with evaluation of the expression
|
||||
*/
|
||||
public Object getValue() throws EvaluationException {
|
||||
EvaluationContext eContext = new StandardEvaluationContext();
|
||||
return ast.getValue(new ExpressionState(eContext));
|
||||
}
|
||||
|
||||
// public Class<?> getValueType() throws ELException {
|
||||
// return ast.getValueType(new ExpressionState());
|
||||
// }
|
||||
|
||||
/**
|
||||
* Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc.
|
||||
* The {@link StandardEvaluationContext} can be sub classed and overridden where necessary, rather than implementing
|
||||
* the entire EvaluationContext interface.
|
||||
*
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @return the value of the expression
|
||||
* @throws SpelException if there is a problem with evaluation of the expression.
|
||||
*/
|
||||
public Object getValue(EvaluationContext context) throws EvaluationException {
|
||||
return ast.getValue(new ExpressionState(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc -
|
||||
* the type of the evaluation result is expected to be of a particular class and an exception will be thrown if it
|
||||
* is not and cannot be converted to that type. The {@link StandardEvaluationContext} can be sub classed and
|
||||
* overridden where necessary, rather than implementing the entire EvaluationContext interface.
|
||||
*
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @param expectedResultType the class the caller would like the result to be
|
||||
* @return the value of the expression
|
||||
* @throws SpelException if there is a problem with evaluation of the expression.
|
||||
*/
|
||||
public Object getValue(EvaluationContext context, Class<?> expectedResultType) throws EvaluationException {
|
||||
Object result = ast.getValue(new ExpressionState(context));
|
||||
|
||||
if (result != null && expectedResultType != null) {
|
||||
Class<?> resultType = result.getClass();
|
||||
if (expectedResultType.isAssignableFrom(resultType)) {
|
||||
return result;
|
||||
}
|
||||
// Attempt conversion to the requested type, may throw an exception
|
||||
return context.getTypeUtils().getTypeConverter().convertValue(result, expectedResultType);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an expression and set the result to the specified value. This only makes sense when working with the
|
||||
* expression against a context.
|
||||
*
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @param value the new value
|
||||
* @return the previous value
|
||||
* @throws SpelException if there is a problem with evaluation of the expression.
|
||||
*/
|
||||
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
|
||||
ast.setValue(new ExpressionState(context), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an expression evaluates to an value that can be set with a value (for example, a property).
|
||||
*
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @return true if the expression supports setValue()
|
||||
* @throws SpelException if there is a problem with evaluation of the expression.
|
||||
*/
|
||||
public boolean isWritable(EvaluationContext context) throws EvaluationException {
|
||||
return ast.isWritable(new ExpressionState(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return return the Abstract Syntax Tree for the expression
|
||||
*/
|
||||
public SpelNode getAST() {
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a string representation of the Abstract Syntax Tree for the expression, this should ideally look like the
|
||||
* input expression, but properly formatted since any unnecessary whitespace will have been discarded during the
|
||||
* parse of the expression.
|
||||
*
|
||||
* @return the string representation of the AST
|
||||
*/
|
||||
public String toStringAST() {
|
||||
return ast.toStringAST();
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context) throws EvaluationException {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
public Object getValue(Class<?> expectedResultType) throws EvaluationException {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Auto-generated method stub");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.antlr.runtime.ANTLRStringStream;
|
||||
import org.antlr.runtime.CommonTokenStream;
|
||||
import org.antlr.runtime.RecognitionException;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.DefaultNonTemplateParserContext;
|
||||
import org.springframework.expression.common.TemplateAwareExpressionParser;
|
||||
import org.springframework.expression.spel.ast.SpelNode;
|
||||
import org.springframework.expression.spel.generated.SpringExpressionsLexer;
|
||||
import org.springframework.expression.spel.generated.SpringExpressionsParser.expr_return;
|
||||
import org.springframework.expression.spel.internal.InternalELException;
|
||||
import org.springframework.expression.spel.internal.SpelTreeAdaptor;
|
||||
import org.springframework.expression.spel.internal.SpringExpressionsLexerExtender;
|
||||
import org.springframework.expression.spel.internal.SpringExpressionsParserExtender;
|
||||
|
||||
/**
|
||||
* Instances of this parser class can process Spring Expression Language format expressions. The result of parsing an
|
||||
* expression is a SpelExpression instance that can be repeatedly evaluated (possibly against different evaluation
|
||||
* contexts) or serialized for later evaluation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class SpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
|
||||
private final SpringExpressionsLexer lexer;
|
||||
private final SpringExpressionsParserExtender parser;
|
||||
|
||||
/**
|
||||
* Should be constructed through the SpelParserFactory
|
||||
*/
|
||||
public SpelExpressionParser() {
|
||||
lexer = new SpringExpressionsLexerExtender();
|
||||
CommonTokenStream tokens = new CommonTokenStream(lexer);
|
||||
parser = new SpringExpressionsParserExtender(tokens);
|
||||
parser.setTreeAdaptor(new SpelTreeAdaptor());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 EvaluationException if the expression is invalid
|
||||
*/
|
||||
protected 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();
|
||||
return new SpelExpression(expressionString, (SpelNode) exprReturn.getTree());
|
||||
} catch (RecognitionException re) {
|
||||
ParseException exception = new ParseException(expressionString, "Recognition error at position: "+re.charPositionInLine+": "+re.getMessage(), re);
|
||||
throw exception;
|
||||
} catch (InternalELException e) {
|
||||
SpelException wrappedException = e.getCause();
|
||||
throw new ParseException(expressionString,"Parsing problem: "+wrappedException.getMessage(),wrappedException);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple override with covariance to return a nicer type
|
||||
*/
|
||||
public SpelExpression parseExpression(String expressionString) throws ParseException {
|
||||
return (SpelExpression)super.parseExpression(expressionString, DefaultNonTemplateParserContext.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* 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 java.text.MessageFormat;
|
||||
|
||||
/**
|
||||
* Contains all the messages that can be produced by the Spring Expression Language. Each message has a kind (info,
|
||||
* warn, error) and a code number. Tests can be written to expect particular code numbers rather than particular text,
|
||||
* enabling the message text to more easily be modified and the tests to run successfully in different locales.
|
||||
* <p>
|
||||
* When a message is formatted, it will have this kind of form
|
||||
*
|
||||
* <pre><code>
|
||||
* EL1004E: (pos 34): Type cannot be found 'String'
|
||||
* </pre></code> The prefix captures the code and the error kind, whilst the position is included if it is known and the
|
||||
* message has had all relevant inserts applied to it.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*
|
||||
*/
|
||||
public enum SpelMessages {
|
||||
// TODO put keys and messages into bundles for easy NLS
|
||||
// TODO damn code formatter keeps messing up the layout
|
||||
|
||||
INITIALIZER_LENGTH_INCORRECT(Kind.ERROR, 1001,
|
||||
"Array constructor call: initializer size of {0} does not match declared length of {1}"), TYPE_CONVERSION_ERROR(
|
||||
Kind.ERROR, 1002, "Type conversion problem, cannot convert from {0} to {1}"), CONSTRUCTOR_NOT_FOUND(
|
||||
Kind.ERROR, 1003, "Constructor call: No suitable constructor on type {0} for arguments {1}"), TYPE_NOT_FOUND(
|
||||
Kind.ERROR, 1004, "Type cannot be found ''{0}''"), ADDITION_NOT_DEFINED(Kind.ERROR, 1005,
|
||||
"Addition not defined between operands of type {0} and {1}"), METHOD_NOT_FOUND(Kind.ERROR, 1006,
|
||||
"Method call: Method {0} cannot be found on {1} type"), ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT(
|
||||
Kind.ERROR, 1007, "Method call: Attempted to call method {0} on null context object"), ATTEMPTED_PROPERTY_FIELD_REF_ON_NULL_CONTEXT_OBJECT(
|
||||
Kind.ERROR, 1008,
|
||||
"Field or property reference: Attempted to refer to field or property ''{0}'' on null context object"),
|
||||
PROPERTY_OR_FIELD_NOT_FOUND(Kind.ERROR, 1009, "Field or property ''{0}'' cannot be found on object of type ''{1}''"),
|
||||
PROPERTY_OR_FIELD_SETTER_NOT_FOUND(Kind.ERROR, 1010, "Field or property ''{0}'' cannot be set on object of type ''{1}''"),
|
||||
MULTIPLY_NOT_DEFINED(
|
||||
Kind.ERROR, 1011, "Multiply not defined between operands of type {0} and {1}"), NOT_COMPARABLE(Kind.ERROR,
|
||||
1012, "Cannot compare instances of {0} and {1}"), NOT_COMPARABLE_CANNOT_COERCE(Kind.ERROR, 1013,
|
||||
"Cannot compare instances of {0} and {1} because they cannot be coerced to the same type"), VARIABLE_NOT_FOUND(
|
||||
Kind.ERROR, 1014, "Variable named ''{0}'' cannot be found"), INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(
|
||||
Kind.ERROR, 1015, "Incorrect number of arguments for function, {0} supplied but function takes {1}"), NO_SUCH_FUNCTION(
|
||||
Kind.ERROR, 1016, "No such function named ''{0}''"), NOT_A_FUNCTION(Kind.ERROR, 1017,
|
||||
"The name ''{0}'' did not map to a function, it mapped to a ''{1}''"), INVALID_TYPE_FOR_SELECTION(
|
||||
Kind.ERROR, 1018, "Cannot perform selection on input data of type ''{0}''"), RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(
|
||||
Kind.ERROR, 1019, "Result of selection criteria is not boolean"), MODULUS_NOT_DEFINED(Kind.ERROR, 1020,
|
||||
"Modulus not defined between operands of type ''{0}'' and ''{1}''"), NULL_OPERAND_TO_OPERATOR(Kind.ERROR,
|
||||
1021, "Operand evaluated to null and that is not supported for this operator"), NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION(
|
||||
Kind.ERROR, 1022, "No array size or initializer was supplied to construct the array"), INCORRECT_ELEMENT_TYPE_FOR_ARRAY(
|
||||
Kind.ERROR, 1023, "The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"), BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(
|
||||
Kind.ERROR, 1024, "Right operand for the 'between' operator has to be a two-element list"), TYPE_NOT_SUPPORTED_BY_PROCESSOR(
|
||||
Kind.ERROR, 1025,
|
||||
"The collection processor ''{0}'' does not understand and input collection of elements of type {1}"), UNABLE_TO_ACCESS_FIELD(
|
||||
Kind.ERROR, 1026, "Unable to access field ''{0}'' on type ''{1}''"), UNABLE_TO_ACCESS_PROPERTY_THROUGH_GETTER(
|
||||
Kind.ERROR, 1027, "Unable to access property ''{0}'' through getter on type ''{1}''"), UNABLE_TO_ACCESS_PROPERTY_THROUGH_SETTER(
|
||||
Kind.ERROR, 1028, "Unable to access property ''{0}'' through setter on type ''{1}''"), INVALID_PATTERN(
|
||||
Kind.ERROR, 1029, "Pattern is not valid ''{0}''"), RECOGNITION_ERROR(Kind.ERROR, 1030,
|
||||
"Recognition error: {0}"), // TODO 2 poor message
|
||||
PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1031, "Projection is not supported on the type ''{0}''"), ARGLIST_SHOULD_NOT_BE_EVALUATED(
|
||||
Kind.ERROR, 1032, "The argument list of a lambda expression should never have getValue() called upon it"), MAPENTRY_SHOULD_NOT_BE_EVALUATED(
|
||||
Kind.ERROR, 1033, "A map entry should never have getValue() called upon it"),
|
||||
EXCEPTION_DURING_PROPERTY_READ(Kind.ERROR, 1034, "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"),
|
||||
EXCEPTION_DURING_CONSTRUCTOR_INVOCATION(
|
||||
Kind.ERROR, 1035, "A problem occurred whilst attempting to construct ''{0}'': ''{1}''"), DATE_CANNOT_BE_PARSED(
|
||||
Kind.ERROR, 1036, "Unable to parse date ''{0}'' using format ''{1}''"), FUNCTION_REFERENCE_CANNOT_BE_INVOKED(
|
||||
Kind.ERROR, 1037, "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"), FUNCTION_NOT_DEFINED(
|
||||
Kind.ERROR, 1038, "The function ''{0}'' could not be found"), EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR,
|
||||
1039, "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"), ARRAY_INDEX_OUT_OF_BOUNDS(
|
||||
Kind.ERROR, 1040, "The array has ''{0}'' elements, index ''{1}'' is invalid"), COLLECTION_INDEX_OUT_OF_BOUNDS(
|
||||
Kind.ERROR, 1041, "The collection has ''{0}'' elements, index ''{1}'' is invalid"), STRING_INDEX_OUT_OF_BOUNDS(
|
||||
Kind.ERROR, 1042, "The string has ''{0}'' characters, index ''{1}'' is invalid"), INDEXING_NOT_SUPPORTED_FOR_TYPE(
|
||||
Kind.ERROR, 1043, "Indexing into type ''{0}'' is not supported"), OPERATOR_IN_CANNOT_DETERMINE_MEMBERSHIP(
|
||||
Kind.ERROR, 1044, "Operator 'in' not implemented for detecting membership of a ''{0}'' in a ''{1}''"), CANNOT_NEGATE_TYPE(
|
||||
Kind.ERROR, 1045, "Cannot determine negation of type ''{0}''"), CUT_ARGUMENTS_MUST_BE_INTS(Kind.ERROR,
|
||||
1046, "Both arguments to the cut() processor must be Integers, but they are ''{0}'' and ''{1}''"), SOUNDSLIKE_NEEDS_STRING_OPERAND(
|
||||
Kind.ERROR, 1047, "The soundslike operator needs String operands, but found a ''{0}''"), IS_OPERATOR_NEEDS_CLASS_OPERAND(
|
||||
Kind.ERROR, 1048, "The operator 'is' needs the right operand to be a class, not a ''{0}''"), LOCAL_VARIABLE_NOT_DEFINED(
|
||||
Kind.ERROR, 1049, "Local variable named ''{0}'' could not be found"), EXCEPTION_DURING_METHOD_INVOCATION(
|
||||
Kind.ERROR, 1050,
|
||||
"A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"), PLACEHOLDER_SHOULD_NEVER_BE_EVALUATED(
|
||||
Kind.ERROR, 1051, "InternalError: A placeholder node in the Ast should never be evaluated!"), OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(
|
||||
Kind.ERROR, 1052, "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"), UNEXPECTED_PROBLEM_INVOKING_OPERATOR(
|
||||
Kind.ERROR, 1054,
|
||||
"Unexpected problem invoking operator ''{0}'' between objects of type ''{1}'' and ''{2}'': {3}"), PROBLEM_LOCATING_METHOD(
|
||||
Kind.ERROR, 1055, "Problem locating method {0} cannot on type {1}"), PROBLEM_LOCATING_CONSTRUCTOR(
|
||||
Kind.ERROR, 1056,
|
||||
"A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"), INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR(
|
||||
Kind.ERROR, 1057, "First operand to like operator must be a string. ''{0}'' is not"), INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR(
|
||||
Kind.ERROR, 1058, "Second operand to like operator must be a string (regex). ''{0}'' is not"),
|
||||
SETVALUE_NOT_SUPPORTED(Kind.ERROR, 1059, "setValue(ExpressionState, Object) not implemented for ''{0}'' (''{1}''"),
|
||||
TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(Kind.ERROR, 1060, "Expected the type of the new array to be specified as a String but found ''{0}''"),
|
||||
PROBLEM_DURING_TYPE_CONVERSION(Kind.ERROR,1061,"Problem occurred during type conversion: {0}"),
|
||||
MULTIPLE_POSSIBLE_METHODS(Kind.ERROR,1062,"Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"),
|
||||
EXCEPTION_DURING_PROPERTY_WRITE(Kind.ERROR,1063,"A problem occurred whilst attempting to set the property ''{0}'': ''{1}''"),
|
||||
;
|
||||
|
||||
private Kind kind;
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
public static enum Kind {
|
||||
INFO, WARNING, ERROR
|
||||
};
|
||||
|
||||
private SpelMessages(Kind kind, int code, String message) {
|
||||
this.kind = kind;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a complete message including the prefix, the position (if known) and with the inserts applied to the
|
||||
* message.
|
||||
*
|
||||
* @param pos the position, if less than zero it is ignored and not included in the message
|
||||
* @param inserts the inserts to put into the formatted message
|
||||
* @return a formatted message
|
||||
*/
|
||||
public String formatMessage(int pos, Object... inserts) {
|
||||
StringBuilder formattedMessage = new StringBuilder();
|
||||
formattedMessage.append("EL").append(code);
|
||||
switch (kind) {
|
||||
case WARNING:
|
||||
formattedMessage.append("W");
|
||||
break;
|
||||
case INFO:
|
||||
formattedMessage.append("I");
|
||||
break;
|
||||
case ERROR:
|
||||
formattedMessage.append("E");
|
||||
break;
|
||||
}
|
||||
formattedMessage.append(":");
|
||||
if (pos != -1) {
|
||||
formattedMessage.append("(pos ").append(pos).append("): ");
|
||||
}
|
||||
formattedMessage.append(MessageFormat.format(this.message, inserts));
|
||||
return formattedMessage.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 java.io.PrintStream;
|
||||
|
||||
import org.springframework.expression.spel.ast.SpelNode;
|
||||
|
||||
/**
|
||||
* Utilities for working with Spring Expressions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*
|
||||
*/
|
||||
public class SpelUtilities {
|
||||
|
||||
/**
|
||||
* Output an indented representation of the expression syntax tree to the specified output stream.
|
||||
*
|
||||
* @param printStream the output stream to print into
|
||||
* @param expression the expression to be displayed
|
||||
*/
|
||||
public static void printAbstractSyntaxTree(PrintStream printStream, SpelExpression expression) {
|
||||
printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST start");
|
||||
printAST(printStream, expression.getAST(), "");
|
||||
printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST end");
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method for printing the AST with indentation
|
||||
*/
|
||||
private static void printAST(PrintStream out, SpelNode t, String indent) {
|
||||
if (t != null) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
String s = null;
|
||||
if (t.getType() == -1)
|
||||
s = "EOF";
|
||||
else {
|
||||
s = t.getClass().getSimpleName();
|
||||
}
|
||||
sb.append(indent + s + (t.getChildCount() < 2 ? "" : " #" + t.getChildCount()));
|
||||
out.println(sb.toString());
|
||||
for (int i = 0; i < t.getChildCount(); i++) {
|
||||
printAST(out, t.getChild(i), indent + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user