First drop of SPEL

This commit is contained in:
Andy Clement 2008-08-12 16:14:43 +00:00
parent ca93824d2b
commit c2624ea05e
115 changed files with 22313 additions and 0 deletions

View File

@ -0,0 +1,68 @@
package org.springframework.expression.common;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
/**
* Represents a template expression broken into pieces. Each piece will be an Expression but pure text parts to the
* template will be represented as LiteralExpression objects. An example of a template expression might be: <code><pre>
* &quot;Hello ${getName()}&quot;
* </pre></code> which will be represented as a CompositeStringExpression of two parts. The first part being a
* LiteralExpression representing 'Hello ' and the second part being a real expression that will call getName() when
* invoked.
* @author Andy Clement
*/
public class CompositeStringExpression implements Expression {
private String expressionString;
/**
* The array of expressions that make up the composite expression
*/
private Expression[] expressions;
public CompositeStringExpression(String expressionString, Expression[] expressions) {
this.expressionString = expressionString;
this.expressions = expressions;
}
public String getExpressionString() {
return expressionString;
}
public String getValue() throws EvaluationException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expressions.length; i++) {
// TODO (asc) is stringify ok for the non literal components? or should the converters be used? see another case below
sb.append(expressions[i].getValue());
}
return sb.toString();
}
public String getValue(EvaluationContext context) throws EvaluationException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expressions.length; i++) {
sb.append(expressions[i].getValue(context));
}
return sb.toString();
}
public Class getValueType(EvaluationContext context) throws EvaluationException {
return String.class;
}
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
throw new EvaluationException(expressionString, "Cannot call setValue() on a composite expression");
}
public Object getValue(EvaluationContext context, Class<?> expectedResultType) throws EvaluationException {
Object value = getValue(context);
return ExpressionUtils.convert(context, value, expectedResultType);
}
public Object getValue(Class<?> expectedResultType) throws EvaluationException {
Object value = getValue();
return ExpressionUtils.convert(null, value, expectedResultType);
}
}

View File

@ -0,0 +1,24 @@
package org.springframework.expression.common;
import org.springframework.expression.ParserContext;
public class DefaultNonTemplateParserContext implements ParserContext {
public static final DefaultNonTemplateParserContext INSTANCE = new DefaultNonTemplateParserContext();
private DefaultNonTemplateParserContext() {
}
public String getExpressionPrefix() {
return null;
}
public String getExpressionSuffix() {
return null;
}
public boolean isTemplate() {
return false;
}
}

View File

@ -0,0 +1,24 @@
package org.springframework.expression.common;
import org.springframework.expression.ParserContext;
public class DefaultTemplateParserContext implements ParserContext {
public static final DefaultTemplateParserContext INSTANCE = new DefaultTemplateParserContext();
private DefaultTemplateParserContext() {
}
public String getExpressionPrefix() {
return "${";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.common;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypeUtils;
/**
* Common utility functions that may be used by any Expression Language provider.
*
* @author Andy Clement
*/
public class ExpressionUtils {
/**
* Determines if there is a type converter available in the specified context and attempts to use it to convert the supplied
* value to the specified type. Throws an exception if conversion is not possible.
*
* @param context the evaluation context that may define a type converter
* @param value the value to convert (may be null)
* @param toType the type to attempt conversion to
* @return the converted value
* @throws EvaluationException if there is a problem during conversion or conversion of the value to the specified type is not supported
*/
public static Object convert(EvaluationContext context, Object value, Class<?> toType) throws EvaluationException {
if (value==null || toType.isAssignableFrom(value.getClass())) {
return value;
}
if (context!=null) {
TypeUtils typeUtils = context.getTypeUtils();
if (typeUtils != null) {
TypeConverter typeConverter = typeUtils.getTypeConverter();
return typeConverter.convertValue(value,toType);
}
}
throw new EvaluationException("Cannot convert value '"+value+"' to type '"+toType.getName()+"'");
}
}

View File

@ -0,0 +1,56 @@
package org.springframework.expression.common;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
/**
* A very simple hardcoded implementation of the Expression interface that represents a string literal. It is used with
* CompositeStringExpression when representing a template expression which is made up of pieces - some being real
* expressions to be handled by an EL implementation like Spel, and some being just textual elements.
*
* @author Andy Clement
*
*/
public class LiteralExpression implements Expression {
/**
* Fixed literal value of this expression
*/
private String literalValue;
public LiteralExpression(String literalValue) {
this.literalValue = literalValue;
}
public String getExpressionString() {
return new StringBuilder().append("'").append(literalValue).append("'").toString();
}
public Object getValue() throws EvaluationException {
return literalValue;
}
public Object getValue(EvaluationContext context) throws EvaluationException {
return literalValue;
}
public Class getValueType(EvaluationContext context) throws EvaluationException {
return String.class;
}
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
throw new EvaluationException(this.literalValue, "Cannot call setValue() on a LiteralExpression");
}
public Object getValue(EvaluationContext context, Class<?> expectedResultType) throws EvaluationException {
Object value = getValue(context);
return ExpressionUtils.convert(context, value, expectedResultType);
}
public Object getValue(Class<?> expectedResultType) throws EvaluationException {
Object value = getValue();
return ExpressionUtils.convert(null, value, expectedResultType);
}
}

View File

@ -0,0 +1 @@
package org.springframework.expression.common; import java.util.LinkedList; import java.util.List; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; import sun.tools.jstat.ParserException; /** * An expression parser that understands templates. It can be subclassed by expression parsers that do not offer first * class support for templating. * * @author Keith Donald * @author Andy Clement */ public abstract class TemplateAwareExpressionParser implements ExpressionParser { public Expression parseExpression(String expressionString) throws ParseException { return parseExpression(expressionString, DefaultNonTemplateParserContext.INSTANCE); } public final Expression parseExpression(String expressionString, ParserContext context) throws ParseException { if (context == null) { context = DefaultNonTemplateParserContext.INSTANCE; } if (context.isTemplate()) { return parseTemplate(expressionString, context); } else { return doParseExpression(expressionString, context); } } private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException { // TODO what about zero length expressions? Expression[] expressions = parseExpressions(expressionString, context); if (expressions.length == 1) { return expressions[0]; } else { return new CompositeStringExpression(expressionString, expressions); } } // helper methods /** * Helper that parses given expression string using the configured parser. The expression string can contain any * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static * pieces of text will also be returned as Expressions that just return that static piece of text. As a result, * evaluating all returned expressions and concatenating the results produces the complete evaluated string. * Unwrapping is only done of the outermost delimiters found, so the string 'hello ${foo${abc}}' would break into * the pieces 'hello ' and 'foo${abc}'. This means that expression languages that used ${..} as part of their * functionality are supported without any problem * * @param expressionString the expression string * @return the parsed expressions * @throws ParserException when the expressions cannot be parsed */ private final Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException { // TODO this needs to handle nested delimiters for cases where the expression uses the delim chars List<Expression> expressions = new LinkedList<Expression>(); int startIdx = 0; String prefix = context.getExpressionPrefix(); String suffix = context.getExpressionSuffix(); while (startIdx < expressionString.length()) { int prefixIndex = expressionString.indexOf(prefix, startIdx); if (prefixIndex >= startIdx) { // a inner expression was found - this is a composite if (prefixIndex > startIdx) { expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex))); startIdx = prefixIndex; } int nextPrefixIndex = expressionString.indexOf(prefix, prefixIndex + prefix.length()); int suffixIndex; if (nextPrefixIndex == -1) { // this is the last expression in the expression string suffixIndex = expressionString.lastIndexOf(suffix); } else { // another expression exists after this one in the expression string suffixIndex = expressionString.lastIndexOf(suffix, nextPrefixIndex); } if (suffixIndex < (prefixIndex + prefix.length())) { throw new ParseException(expressionString, "No ending suffix '" + suffix + "' for expression starting at character " + prefixIndex + ": " + expressionString.substring(prefixIndex), null); } else if (suffixIndex == prefixIndex + prefix.length()) { throw new ParseException(expressionString, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex, null); } else { String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex); expressions.add(doParseExpression(expr, context)); startIdx = suffixIndex +suffix.length(); } } else { if (startIdx == 0) { expressions.add(doParseExpression(expressionString, context)); } else { // no more ${expressions} found in string, add rest as static text expressions.add(new LiteralExpression(expressionString.substring(startIdx))); } startIdx = expressionString.length(); } } return expressions.toArray(new Expression[expressions.size()]); } protected abstract Expression doParseExpression(String expressionString, ParserContext context) throws ParseException; }

View File

@ -0,0 +1,69 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents the list of arguments supplied to a lambda expression. For an expression "{|x,y| $x > $y ? $x : $y }" the
* argument list is x,y
*
* @author Andy Clement
*
*/
public class ArgList extends SpelNode {
public ArgList(Token payload) {
super(payload);
}
/**
* @return a list of the argument names captured in this ArgList
*/
public List<String> getArgumentNames() {
if (getChildCount() == 0)
return Collections.emptyList();
List<String> result = new ArrayList<String>();
for (int i = 0; i < getChildCount(); i++) {
result.add(getChild(i).getText());
}
return result;
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(",");
sb.append(getChild(i).toStringAST());
}
return sb.toString();
}
@Override
public Object getValue(ExpressionState state) throws SpelException {
throw new SpelException(SpelMessages.ARGLIST_SHOULD_NOT_BE_EVALUATED);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents assignment. An alternative to calling setValue() for an expression is to use an assign.
* <p>
* Example: 'someNumberProperty=42'
*
* @author Andy Clement
*
*/
public class Assign extends SpelNode {
public Assign(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object newValue = getChild(1).getValue(state);
getChild(0).setValue(state, newValue);
return newValue;
}
@Override
public String toStringAST() {
return new StringBuilder().append(getChild(0).toStringAST()).append("=").append(getChild(1).toStringAST())
.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
/**
* Represents the literal values TRUE and FALSE.
*
* @author Andy Clement
*
*/
public class BooleanLiteral extends Literal {
private final Boolean value;
public BooleanLiteral(Token payload, boolean value) {
super(payload);
this.value = value;
}
@Override
public Boolean getLiteralValue() {
return value;
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'
*
* @author Andy Clement
*
*/
public class CompoundExpression extends SpelNode {
public CompoundExpression(Token payload) {
super(payload);
}
/**
* Evalutes a compound expression. This involves evaluating each piece in turn and the return value from each piece
* is the active context object for the subsequent piece.
*
* @param state the state in which the expression is being evaluated
* @return the final value from the last piece of the compound expression
*/
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object result = null;
SpelNode nextNode = null;
try {
nextNode = getChild(0);
result = nextNode.getValue(state);
for (int i = 1; i < getChildCount(); i++) {
try {
state.pushActiveContextObject(result);
nextNode = getChild(i);
result = nextNode.getValue(state);
} finally {
state.popActiveContextObject();
}
}
} catch (SpelException ee) {
// Correct the position for the error before rethrowing
ee.setPosition(nextNode.getCharPositionInLine());
throw ee;
}
return result;
}
@Override
public void setValue(ExpressionState state, Object value) throws EvaluationException {
if (getChildCount() == 1) {
getChild(0).setValue(state, value);
return;
}
Object ctx = getChild(0).getValue(state);
for (int i = 1; i < getChildCount() - 1; i++) {
try {
state.pushActiveContextObject(ctx);
ctx = getChild(i).getValue(state);
} finally {
state.popActiveContextObject();
}
}
try {
state.pushActiveContextObject(ctx);
getChild(getChildCount() - 1).setValue(state, value);
} finally {
state.popActiveContextObject();
}
}
@Override
public boolean isWritable(ExpressionState state) throws EvaluationException {
if (getChildCount() == 1) {
return getChild(0).isWritable(state);
}
Object ctx = getChild(0).getValue(state);
for (int i = 1; i < getChildCount() - 1; i++) {
try {
state.pushActiveContextObject(ctx);
ctx = getChild(i).getValue(state);
} finally {
state.popActiveContextObject();
}
}
try {
state.pushActiveContextObject(ctx);
return getChild(getChildCount() - 1).isWritable(state);
} finally {
state.popActiveContextObject();
}
}
@Override
public String toStringAST() {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < getChildCount(); i++) {
sb.append(getChild(i).toStringAST());
}
return sb.toString();
}
}

View File

@ -0,0 +1,312 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.lang.reflect.Array;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.internal.TypeCode;
import org.springframework.expression.spel.internal.Utils;
/**
* Represents the invocation of a constructor. Either a constructor on a regular type or construction of an array. When
* an array is constructed, an initializer can be specified.
* <p>
* Examples:<br>
* new String('hello world')<br>
* new int[]{1,2,3,4}<br>
* new int[3] new int[3]{1,2,3}
*
* @author Andy Clement
*
*/
public class ConstructorReference extends SpelNode {
/**
* The resolver/executor model {@link ConstructorResolver} supports the caching of executor objects that can run
* some discovered constructor repeatedly without searching for it each time. This flag controls whether caching
* occurs and is primarily exposed for testing.
*/
public static boolean useCaching = true;
/**
* The cached executor that may be reused on subsequent evaluations.
*/
private ConstructorExecutor cachedExecutor;
/**
* If true then this is an array constructor, for example, 'new String[]', rather than a simple constructor 'new
* String()'
*/
private final boolean isArrayConstructor;
public ConstructorReference(Token payload, boolean isArrayConstructor) {
super(payload);
this.isArrayConstructor = isArrayConstructor;
}
/**
* Implements getValue() - delegating to the code for building an array or a simple type.
*/
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
if (isArrayConstructor) {
return createArray(state);
} else {
return createNewInstance(state);
}
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("new ");
int index = 0;
sb.append(getChild(index++).toStringAST());
if (!isArrayConstructor) {
sb.append("(");
for (int i = index; i < getChildCount(); i++) {
if (i > index)
sb.append(",");
sb.append(getChild(i).toStringAST());
}
sb.append(")");
} else {
// Next child is EXPRESSIONLIST token with children that are the
// expressions giving array size
sb.append("[");
SpelNode arrayRank = getChild(index++);
for (int i = 0; i < arrayRank.getChildCount(); i++) {
if (i > 0)
sb.append(",");
sb.append(arrayRank.getChild(i).toStringAST());
}
sb.append("]");
if (index < getChildCount())
sb.append(" ").append(getChild(index).toStringAST());
}
return sb.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
/**
* Create an array and return it. The children of this node indicate the type of array, the array ranks and any optional initializer that might have been supplied.
*
* @param state the expression state within which this expression is being evaluated
* @return the new array
* @throws EvaluationException if there is a problem creating the array
*/
private Object createArray(ExpressionState state) throws EvaluationException {
Object intendedArrayType = getChild(0).getValue(state);
if (!(intendedArrayType instanceof String)) {
throw new SpelException(getChild(0).getCharPositionInLine(),SpelMessages.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION,Utils.formatClassnameForMessage(intendedArrayType.getClass()));
}
String type = (String) intendedArrayType;
Class<?> componentType = null;
TypeCode arrayTypeCode = TypeCode.forName(type);
if (arrayTypeCode == TypeCode.OBJECT) {
componentType = state.findType(type);
} else {
componentType = arrayTypeCode.getType();
}
Object newArray = null;
if (getChild(1).getChildCount() == 0) { // are the array ranks defined?
if (getChildCount() < 3) {
throw new SpelException(getCharPositionInLine(), SpelMessages.NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION);
}
// no array ranks so use the size of the initializer to determine array size
int arraySize = getChild(2).getChildCount();
newArray = Array.newInstance(componentType, arraySize);
} else {
// Array ranks are specified but is it a single or multiple dimension array?
int dimensions = getChild(1).getChildCount();
if (dimensions == 1) {
Object o = getChild(1).getValue(state);
int arraySize = state.toInteger(o);
if (getChildCount() == 3) {
// Check initializer length matches array size length
int initializerLength = getChild(2).getChildCount();
if (initializerLength != arraySize) {
throw new SpelException(getChild(2).getCharPositionInLine(), SpelMessages.INITIALIZER_LENGTH_INCORRECT,
initializerLength, arraySize);
}
}
newArray = Array.newInstance(componentType, arraySize);
} else {
// Multi-dimensional - hold onto your hat !
int[] dims = new int[dimensions];
for (int d=0; d<dimensions;d++) {
dims[d] = state.toInteger(getChild(1).getChild(d).getValue(state));
}
newArray = Array.newInstance(componentType,dims);
// TODO check any specified initializer matches
}
}
// Populate the array using the initializer if one is specified
if (getChildCount() == 3) {
SpelNode initializer = getChild(2);
if (arrayTypeCode == TypeCode.OBJECT) {
Object[] newObjectArray = (Object[]) newArray;
for (int i = 0; i < newObjectArray.length; i++) {
SpelNode elementNode = initializer.getChild(i);
Object arrayEntry = elementNode.getValue(state);
if (!componentType.isAssignableFrom(arrayEntry.getClass())) {
throw new SpelException(elementNode.getCharPositionInLine(),
SpelMessages.INCORRECT_ELEMENT_TYPE_FOR_ARRAY, componentType.getName(), arrayEntry
.getClass().getName());
}
newObjectArray[i] = arrayEntry;
}
} else if (arrayTypeCode == TypeCode.INT) {
int[] newIntArray = (int[]) newArray;
for (int i = 0; i < newIntArray.length; i++) {
newIntArray[i] = state.toInteger(initializer.getChild(i).getValue(state));
}
} else if (arrayTypeCode == TypeCode.BOOLEAN) {
boolean[] newBooleanArray = (boolean[]) newArray;
for (int i = 0; i < newBooleanArray.length; i++) {
newBooleanArray[i] = state.toBoolean(initializer.getChild(i).getValue(state));
}
} else if (arrayTypeCode == TypeCode.CHAR) {
char[] newCharArray = (char[]) newArray;
for (int i = 0; i < newCharArray.length; i++) {
newCharArray[i] = state.toCharacter(initializer.getChild(i).getValue(state));
}
} else if (arrayTypeCode == TypeCode.SHORT) {
short[] newShortArray = (short[]) newArray;
for (int i = 0; i < newShortArray.length; i++) {
newShortArray[i] = state.toShort(initializer.getChild(i).getValue(state));
}
} else if (arrayTypeCode == TypeCode.LONG) {
long[] newLongArray = (long[]) newArray;
for (int i = 0; i < newLongArray.length; i++) {
newLongArray[i] = state.toLong(initializer.getChild(i).getValue(state));
}
} else if (arrayTypeCode == TypeCode.FLOAT) {
float[] newFloatArray = (float[]) newArray;
for (int i = 0; i < newFloatArray.length; i++) {
newFloatArray[i] = state.toFloat(initializer.getChild(i).getValue(state));
}
} else if (arrayTypeCode == TypeCode.DOUBLE) {
double[] newDoubleArray = (double[]) newArray;
for (int i = 0; i < newDoubleArray.length; i++) {
newDoubleArray[i] = state.toDouble(initializer.getChild(i).getValue(state));
}
} else if (arrayTypeCode == TypeCode.BYTE) {
byte[] newByteArray = (byte[]) newArray;
for (int i = 0; i < newByteArray.length; i++) {
newByteArray[i] = state.toByte(initializer.getChild(i).getValue(state));
}
}
}
return newArray;
}
/**
* Create a new ordinary object and return it.
*
* @param state the expression state within which this expression is being evaluated
* @return the new object
* @throws EvaluationException if there is a problem creating the object
*/
private Object createNewInstance(ExpressionState state) throws EvaluationException {
Object[] arguments = new Object[getChildCount() - 1];
Class<?>[] argumentTypes = new Class[getChildCount() - 1];
for (int i = 0; i < arguments.length; i++) {
Object childValue = getChild(i + 1).getValue(state);
arguments[i] = childValue;
argumentTypes[i] = childValue.getClass();
}
if (cachedExecutor != null) {
try {
return cachedExecutor.execute(state.getEvaluationContext(), arguments);
} catch (AccessException ae) {
// this is OK - it may have gone stale due to a class change,
// let's try to get a new one and call it before giving up
}
}
// either there was no accessor or it no longer exists
String typename = (String) getChild(0).getValue(state);
cachedExecutor = findExecutorForConstructor(typename, argumentTypes, state);
try {
return cachedExecutor.execute(state.getEvaluationContext(), arguments);
} catch (AccessException ae) {
throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_CONSTRUCTOR_INVOCATION, typename, ae.getMessage());
} finally {
if (!useCaching) {
cachedExecutor = null;
}
}
}
/**
* Go through the list of registered constructor resolvers and see if any can find a constructor that takes the specified set of arguments.
*
* @param typename the type trying to be constructed
* @param argumentTypes the types of the arguments supplied that the constructor must take
* @param state the current state of the expression
* @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
* @throws SpelException if there is a problem locating the constructor
*/
public ConstructorExecutor findExecutorForConstructor(String typename, Class<?>[] argumentTypes, ExpressionState state) throws SpelException {
EvaluationContext eContext = state.getEvaluationContext();
List<ConstructorResolver> cResolvers = eContext.getConstructorResolvers();
if (cResolvers != null) {
for (ConstructorResolver ctorResolver : cResolvers) {
try {
ConstructorExecutor cEx = ctorResolver.resolve(state.getEvaluationContext(), typename,
argumentTypes);
if (cEx != null) {
return cEx;
}
} catch (AccessException e) {
Throwable cause = e.getCause();
if (cause instanceof SpelException) {
throw (SpelException) cause;
} else {
throw new SpelException(cause, SpelMessages.PROBLEM_LOCATING_CONSTRUCTOR, typename, Utils
.formatMethodForMessage("", argumentTypes));
}
}
}
}
throw new SpelException(SpelMessages.CONSTRUCTOR_NOT_FOUND, typename, Utils.formatMethodForMessage("",
argumentTypes));
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a date literal value in an expression (a java.util.Date object).
*
* @author Andy Clement
*
*/
public class DateLiteral extends SpelNode {
private DateFormat formatter = null;
private Date formattedDate = null;
public DateLiteral(Token payload) {
super(payload);
}
@Override
public Date getValue(ExpressionState state) throws EvaluationException {
if (formatter == null) {
if (getChildCount() > 1) {
formatter = new SimpleDateFormat((String) getChild(1).getValue(state));
} else {
formatter = DateFormat.getDateTimeInstance();
}
}
String valueToParse = (String) getChild(0).getValue(state);
try {
formattedDate = formatter.parse(valueToParse);
} catch (ParseException e) {
throw new SpelException(getCharPositionInLine(), e, SpelMessages.DATE_CANNOT_BE_PARSED, valueToParse,
(formatter instanceof SimpleDateFormat ? ((SimpleDateFormat) formatter).toLocalizedPattern()
: formatter));
}
return formattedDate;
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("date(");
sb.append(getChild(0).toStringAST());
if (getChildCount() > 1) {
sb.append(",").append(getChild(1).toStringAST());
}
sb.append(")");
return sb.toString();
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* This is used for preserving positional information from the input expression.
*
* @author Andy Clement
*
*/
public class Dot extends SpelNode {
// TODO Keep Dot for the positional information or remove it?
public Dot(Token payload) {
super(payload);
}
@Override
public String toStringAST() {
return ".";
}
@Override
public Object getValue(ExpressionState state) throws SpelException {
// This makes Dot a do-nothing operation, but this is not free in terms of computation
return state.getActiveContextObject();
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a list of expressions of the form "(expression1;expression2;expression3)". The expressions are always
* evaluated from left to right, due to possible side effects that earlier expressions may have that influence the
* evaluation of later expressions (defining functions, setting variables, etc).
*
* @author Andy Clement
*
*/
public class ExpressionListNode extends SpelNode {
public ExpressionListNode(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object result = null;
for (int i = 0; i < getChildCount(); i++) {
result = getChild(i).getValue(state);
}
return result;
}
@Override
public boolean isWritable(ExpressionState state) throws EvaluationException {
boolean isWritable = false;
if (getChildCount() > 0) {
// Evaluate all but the last one
for (int i = 0; i < getChildCount() - 1; i++) {
getChild(i).getValue(state);
}
isWritable = getChild(getChildCount() - 1).isWritable(state);
}
return isWritable;
}
@Override
public String toStringAST() {
StringBuffer sb = new StringBuffer();
if (getChildCount() == 1) {
sb.append(getChild(0).toStringAST());
} else {
sb.append("(");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(";");
sb.append(getChild(i).toStringAST());
}
sb.append(")");
}
return sb.toString();
}
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in the context prior to the
* expression being evaluated or within the expression itself using a lambda function definition. For example: Lambda
* function definition in an expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function:
* "#isEven(37)"
*
* Functions are very simplistic, the arguments are not part of the definition (right now), so the names must be unique.
*
* @author Andy Clement
*
*/
public class FunctionReference extends SpelNode {
private final String name;
public FunctionReference(Token payload) {
super(payload);
name = payload.getText();
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object o = state.lookupVariable(name);
if (o == null) {
throw new SpelException(SpelMessages.FUNCTION_NOT_DEFINED, name);
}
if (!(o instanceof Lambda || o instanceof Method)) {
throw new SpelException(SpelMessages.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, name, o.getClass());
}
// FUNCTION REF NEEDS TO DO ARG CONVERSION??
if (o instanceof Lambda) {
return executeLambdaFunction(state, (Lambda) o);
} else { // o instanceof Method
return executeFunctionJLRMethod(state, (Method) o);
}
}
/* Execute a function represented as a java.lang.reflect.Method */
private Object executeFunctionJLRMethod(ExpressionState state, Method m) throws EvaluationException {
Object[] functionArgs = getArguments(state);
if (m.getParameterTypes().length != functionArgs.length) {
throw new SpelException(SpelMessages.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, functionArgs.length, m
.getParameterTypes().length);
}
// Check if arguments need converting
Class<?>[] expectedParams = m.getParameterTypes();
Object[] argsToPass = new Object[functionArgs.length];
TypeConverter converter = state.getEvaluationContext().getTypeUtils().getTypeConverter();
for (int arg=0; arg<argsToPass.length; arg++) {
if (functionArgs[arg]==null || functionArgs[arg].getClass()==expectedParams[arg]) {
argsToPass[arg]=functionArgs[arg];
} else {
argsToPass[arg]=converter.convertValue(functionArgs[arg], expectedParams[arg]);
}
}
try {
return m.invoke(m.getClass(), argsToPass);
} catch (IllegalArgumentException e) {
throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
.getMessage());
} catch (IllegalAccessException e) {
throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
.getMessage());
} catch (InvocationTargetException e) {
throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
.getMessage());
}
}
/*
* Execute a function that was defined as a lambda function.
*/
private Object executeLambdaFunction(ExpressionState state, Lambda lambdaExpression) throws EvaluationException {
Object[] functionArgs = getArguments(state);
List<String> args = lambdaExpression.getArguments();
if (args.size() != functionArgs.length) {
throw new SpelException(SpelMessages.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, functionArgs.length, args
.size());
}
Map<String, Object> argMap = new HashMap<String, Object>();
for (int i = 0; i < args.size(); i++) {
argMap.put(args.get(i), functionArgs[i]);
}
try {
state.enterScope(argMap);
return ((SpelNode) lambdaExpression.getExpression()).getValue(state);
} finally {
state.exitScope();
}
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder("#").append(name);
sb.append("(");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(",");
sb.append(getChild(i).toStringAST());
}
sb.append(")");
return sb.toString();
}
// to 'assign' to a function don't use the () suffix and so it is just a variable reference
@Override
public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
return false;
}
/**
* Compute the arguments to the function, they are the children of this expression node.
* @return an array of argument values for the function call
*/
private Object[] getArguments(ExpressionState state) throws EvaluationException {
// Compute arguments to the function
Object[] arguments = new Object[getChildCount()];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = getChild(i).getValue(state);
}
return arguments;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
public class Identifier extends SpelNode {
private final String id;
public Identifier(Token payload) {
super(payload);
id = payload.getText();
}
@Override
public String toStringAST() {
return id;
}
@Override
public String getValue(ExpressionState state) throws SpelException {
return id;
}
}

View File

@ -0,0 +1,156 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.Collection;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
// TODO 2 support multidimensional arrays
// TODO 3 support correct syntax for multidimensional [][][] and not [,,,]
/**
* An Indexer can index into some proceeding structure to access a particular piece of it. Supported structures are:
* strings/collections (lists/sets)/arrays
*
* @author Andy Clement
*/
public class Indexer extends SpelNode {
public Indexer(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object ctx = state.getActiveContextObject();
Object index = getChild(0).getValue(state);
// Indexing into a Map
if (ctx instanceof Map) {
return ((Map<?, ?>) ctx).get(index);
}
int idx = state.toInteger(index);
if (ctx.getClass().isArray()) {
return accessArrayElement(ctx, idx);
} else if (ctx instanceof Collection) {
Collection<?> c = (Collection<?>) ctx;
if (idx >= c.size()) {
throw new SpelException(SpelMessages.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
}
int pos = 0;
for (Object o : c) {
if (pos == idx) {
return o;
}
pos++;
}
// } else if (ctx instanceof Map) {
// Map<?,?> c = (Map<?,?>) ctx;
// // This code would allow a key/value pair to be pulled out by index from a map
// if (idx >= c.size()) {
// throw new ELException(ELMessages.COLLECTION_INDEX_OUT_OF_BOUNDS,c.size(),idx);
// }
// Set<?> keys = c.keySet();
// int pos = 0;
// for (Object k : keys) {
// if (pos==idx) {
// return new KeyValuePair(k,c.get(k));
// }
// pos++;
// }
} else if (ctx instanceof String) {
String ctxString = (String) ctx;
if (idx >= ctxString.length()) {
throw new SpelException(SpelMessages.STRING_INDEX_OUT_OF_BOUNDS, ctxString.length(), idx);
}
return String.valueOf(ctxString.charAt(idx));
}
throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, ctx.getClass().getName());
}
private Object accessArrayElement(Object ctx, int idx) throws SpelException {
Class<?> arrayComponentType = ctx.getClass().getComponentType();
if (arrayComponentType == Integer.TYPE) {
int[] array = (int[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else if (arrayComponentType == Boolean.TYPE) {
boolean[] array = (boolean[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else if (arrayComponentType == Character.TYPE) {
char[] array = (char[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else if (arrayComponentType == Long.TYPE) {
long[] array = (long[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else if (arrayComponentType == Short.TYPE) {
short[] array = (short[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else if (arrayComponentType == Double.TYPE) {
double[] array = (double[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else if (arrayComponentType == Float.TYPE) {
float[] array = (float[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else if (arrayComponentType == Byte.TYPE) {
byte[] array = (byte[]) ctx;
checkAccess(array.length, idx);
return array[idx];
} else {
Object[] array = (Object[]) ctx;
checkAccess(array.length, idx);
return array[idx];
}
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(",");
sb.append(getChild(i).toStringAST());
}
sb.append("]");
return sb.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
private void checkAccess(int arrayLength, int index) throws SpelException {
if (index > arrayLength) {
throw new SpelException(getCharPositionInLine(), SpelMessages.ARRAY_INDEX_OUT_OF_BOUNDS, arrayLength, index);
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
public class IntLiteral extends Literal {
private static String[] suffixes = {"UL" , "LU" , "ul" , "lu" , "uL" , "lU" , "U" , "L" , "u" , "l" };
private Integer value;
public IntLiteral(Token payload) {
super(payload);
// TODO properly support longs and unsigned numbers
String toParse = payload.getText();
try {
value = Integer.parseInt(toParse);
} catch (NumberFormatException nfe) {
for (int i=0;i<suffixes.length;i++) {
if (toParse.endsWith(suffixes[i])) {
value = Integer.parseInt(toParse.substring(0,toParse.length()-suffixes[i].length()));
return;
}
}
throw nfe;
}
}
public IntLiteral(Token payload, int radix) {
super(payload);
value = Integer.parseInt(payload.getText().substring(2), radix);
}
@Override
public Integer getLiteralValue() {
return value;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.Collections;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represent a Lambda expression, eg. "{|x,y| $x > $y ? $x : $y }". It is possible for an expression to have zero
* arguments in which case this expression node only has one child.
*
* @author Andy Clement
*/
public class Lambda extends SpelNode {
public Lambda(Token payload) {
super(payload);
// payload.setText("LambdaExpression");
}
@Override
public Object getValue(ExpressionState state) throws SpelException {
return this;
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
if (getChildCount() == 1) { // there are no arguments
sb.append("{|| ");
sb.append(getChild(0).toStringAST());
sb.append(" }");
} else {
sb.append("{|");
sb.append(getChild(0).toStringAST());
sb.append("| ");
sb.append(getChild(1).toStringAST());
sb.append(" }");
}
return sb.toString();
}
@Override
public String toString() {
return toStringAST();
}
public List<String> getArguments() {
// Only one child means there are no arguments
if (getChildCount() < 2) {
return Collections.emptyList();
}
ArgList args = (ArgList) getChild(0);
return args.getArgumentNames();
}
public Object getExpression() {
return (getChildCount() > 1 ? getChild(1) : getChild(0));
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
public class ListInitializer extends SpelNode {
public ListInitializer(Token payload) {
super(payload);
}
/**
* Evaluate the list initializer, returning a List<Object>
*/
@Override
public List<Object> getValue(ExpressionState state) throws EvaluationException {
List<Object> result = new ArrayList<Object>();
for (int i = 0; i < getChildCount(); i++) {
Object element = getChild(i).getValue(state);
result.add(element);
}
return result;
}
/**
* Return string form of this node {<child>,<child>,<child>}
*/
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("{");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0) {
sb.append(",");
}
sb.append(getChild(i).toStringAST());
}
sb.append("}");
return sb.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Common superclass for nodes representing literals (boolean, string, number, etc).
*
* @author Andy Clement
*
*/
public abstract class Literal extends SpelNode {
public Literal(Token payload) {
super(payload);
}
public abstract Object getLiteralValue();
@Override
public final Object getValue(ExpressionState state) throws SpelException {
return getLiteralValue();
}
@Override
public String toString() {
return getLiteralValue().toString();
}
@Override
public String toStringAST() {
return toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -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.ast;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Local functions are references like $fn() where fn is the 'local' to lookup in the local scope Example: "(#sqrt={|n|
* T(Math).sqrt($n)};#delegate={|f,n| $f($n)};#delegate(#sqrt,4))"
*
* @author Andy Clement
*/
public class LocalFunctionReference extends SpelNode {
private final String name;
public LocalFunctionReference(Token payload) {
super(payload);
name = payload.getText();
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object o = state.lookupLocalVariable(name);
if (o == null) {
throw new SpelException(SpelMessages.FUNCTION_NOT_DEFINED, name);
}
if (!(o instanceof Lambda)) {
throw new SpelException(SpelMessages.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, name, o.getClass().getName());
}
Object[] arguments = new Object[getChildCount()];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = getChild(i).getValue(state);
}
Lambda lambdaExpression = (Lambda) o;
List<String> args = lambdaExpression.getArguments();
Map<String, Object> argMap = new HashMap<String, Object>();
if (args.size() != arguments.length) {
throw new SpelException(getCharPositionInLine(), SpelMessages.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
arguments.length, args.size());
}
for (int i = 0; i < args.size(); i++) {
argMap.put(args.get(i), arguments[i]);
}
try {
state.enterScope(argMap);
return ((SpelNode) lambdaExpression.getExpression()).getValue(state);
} finally {
state.exitScope();
}
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder("$").append(name);
sb.append("(");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(",");
sb.append(getChild(i).toStringAST());
}
sb.append(")");
return sb.toString();
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* A variable reference such as $someVar. Local variables are only visible at the current scoping level or below within
* an expression. Calling a function introduces a new nested scope.
*
* @author Andy Clement
*
*/
public class LocalVariableReference extends SpelNode {
private final String name;
public LocalVariableReference(Token payload) {
super(payload);
name = payload.getText();
}
@Override
public Object getValue(ExpressionState state) throws SpelException {
Object result = state.lookupLocalVariable(name);
if (result == null) {
throw new SpelException(getCharPositionInLine(), SpelMessages.LOCAL_VARIABLE_NOT_DEFINED, name);
}
return result;
}
@Override
public void setValue(ExpressionState state, Object value) throws SpelException {
// Object oldValue = state.lookupVariable(name);
state.setLocalVariable(name, value);
}
@Override
public String toStringAST() {
return new StringBuilder("$").append(name).toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return true;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents an entry in a map initializer structure like "#{'a':3,'b':2}" Both "'a':3" and "'b':2" would be MapEntry
* instances.
*
* @author Andy Clement
*
*/
public class MapEntry extends SpelNode {
public MapEntry(Token payload) {
super(payload);
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
String k = getChild(0).toStringAST();
String v = getChild(1).toStringAST();
sb.append(k).append(":").append(v);
return sb.toString();
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
throw new SpelException(SpelMessages.MAPENTRY_SHOULD_NOT_BE_EVALUATED);
}
/**
* Return the value of the key for this map entry.
*/
public Object getKeyValue(ExpressionState state) throws EvaluationException {
return getChild(0).getValue(state);
}
/**
* Return the value of the value for this map entry.
*/
public Object getValueValue(ExpressionState state) throws EvaluationException {
return getChild(1).getValue(state);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.HashMap;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
public class MapInitializer extends SpelNode {
public MapInitializer(Token payload) {
super(payload);
}
@Override
public Map<Object, Object> getValue(ExpressionState state) throws EvaluationException {
Map<Object, Object> result = new HashMap<Object, Object>();
for (int i = 0; i < getChildCount(); i++) {
MapEntry mEntry = (MapEntry) getChild(i);
result.put(mEntry.getKeyValue(state), mEntry.getValueValue(state));
}
return result;
}
/**
* Return string form of this node #{a:b,c:d,...}
*/
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("#{");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(", ");
sb.append(getChild(i).toStringAST());
}
sb.append("}");
return sb.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,323 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.internal.Utils;
import org.springframework.expression.spel.processors.AverageProcessor;
import org.springframework.expression.spel.processors.CountProcessor;
import org.springframework.expression.spel.processors.CutProcessor;
import org.springframework.expression.spel.processors.DataProcessor;
import org.springframework.expression.spel.processors.DistinctProcessor;
import org.springframework.expression.spel.processors.MaxProcessor;
import org.springframework.expression.spel.processors.MinProcessor;
import org.springframework.expression.spel.processors.NonNullProcessor;
import org.springframework.expression.spel.processors.SortProcessor;
public class MethodReference extends SpelNode {
private static Map<String, DataProcessor> registeredProcessers = new HashMap<String, DataProcessor>();
private final String name;
private MethodExecutor fastInvocationAccessor; // TODO should this be nulled if executing in a different context or is it OK to keep?
static {
registeredProcessers.put("count", new CountProcessor());
registeredProcessers.put("max", new MaxProcessor());
registeredProcessers.put("min", new MinProcessor());
registeredProcessers.put("average", new AverageProcessor());
registeredProcessers.put("sort", new SortProcessor());
registeredProcessers.put("nonnull", new NonNullProcessor());
registeredProcessers.put("distinct", new DistinctProcessor());
registeredProcessers.put("cut", new CutProcessor());
}
public MethodReference(Token payload) {
super(payload);
name = payload.getText();
}
@SuppressWarnings("unchecked")
private Object invokeDataProcessor(Object[] arguments, ExpressionState state) throws EvaluationException {
DataProcessor processor = registeredProcessers.get(name);
Object target = state.getActiveContextObject();
// Prepare the input, translating arrays to lists
boolean wasArray = false;
Class<?> arrayElementType = null;
Collection<Object> dataToProcess = null;
if (target instanceof Collection) {
dataToProcess = (Collection<Object>) target;
} else {
wasArray = true;
arrayElementType = target.getClass().getComponentType();
if (arrayElementType.equals(Integer.TYPE)) {
int[] data = (int[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else if (arrayElementType.equals(Byte.TYPE)) {
byte[] data = (byte[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else if (arrayElementType.equals(Character.TYPE)) {
char[] data = (char[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else if (arrayElementType.equals(Double.TYPE)) {
double[] data = (double[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else if (arrayElementType.equals(Long.TYPE)) {
long[] data = (long[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else if (arrayElementType.equals(Float.TYPE)) {
float[] data = (float[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else if (arrayElementType.equals(Short.TYPE)) {
short[] data = (short[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else if (arrayElementType.equals(Boolean.TYPE)) {
boolean[] data = (boolean[]) target;
dataToProcess = new ArrayList<Object>();
for (int i = 0; i < data.length; i++) {
dataToProcess.add(data[i]);
}
} else {
dataToProcess = Arrays.asList((Object[]) target);
}
}
Object result = processor.process(dataToProcess, arguments, state);
// Convert the result back if necessary
if (wasArray && (result instanceof Collection)) {
Collection c = (Collection) result;
if (arrayElementType.equals(Integer.TYPE)) {
int[] newArray = (int[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Integer) i).intValue();
return newArray;
} else if (arrayElementType.equals(Byte.TYPE)) {
byte[] newArray = (byte[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Byte) i).byteValue();
return newArray;
} else if (arrayElementType.equals(Character.TYPE)) {
char[] newArray = (char[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Character) i).charValue();
return newArray;
} else if (arrayElementType.equals(Double.TYPE)) {
double[] newArray = (double[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Double) i).doubleValue();
return newArray;
} else if (arrayElementType.equals(Long.TYPE)) {
long[] newArray = (long[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Long) i).longValue();
return newArray;
} else if (arrayElementType.equals(Float.TYPE)) {
float[] newArray = (float[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Float) i).floatValue();
return newArray;
} else if (arrayElementType.equals(Short.TYPE)) {
short[] newArray = (short[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Short) i).shortValue();
return newArray;
} else if (arrayElementType.equals(Boolean.TYPE)) {
boolean[] newArray = (boolean[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c)
newArray[idx++] = ((Boolean) i).booleanValue();
return newArray;
} else {
Object[] newArray = (Object[]) Array.newInstance(arrayElementType, c.size());
int idx = 0;
for (Object i : c) {
newArray[idx++] = i;
}
return newArray;
}
}
return result;
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object currentContext = state.getActiveContextObject();
Object[] arguments = new Object[getChildCount()];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = getChild(i).getValue(state);
}
if (currentContext == null) {
throw new SpelException(getCharPositionInLine(), SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT,
formatMethodForMessage(name, getTypes(arguments)));
}
boolean usingProcessor = registeredProcessers.containsKey(name);
if ((currentContext.getClass().isArray() && usingProcessor)
|| (currentContext instanceof Collection && registeredProcessers.containsKey(name))) {
return invokeDataProcessor(arguments, state);
}
if (fastInvocationAccessor != null) {
try {
return fastInvocationAccessor.execute(state.getEvaluationContext(), state.getActiveContextObject(),
arguments);
} catch (AccessException ae) {
// this is OK - it may have gone stale due to a class change, let's get a new one and retry before
// giving up
}
}
// either there was no accessor or it no longer existed
fastInvocationAccessor = findAccessorForMethod(name, getTypes(arguments), state);
try {
return fastInvocationAccessor.execute(state.getEvaluationContext(), state.getActiveContextObject(),
arguments);
} catch (AccessException ae) {
ae.printStackTrace();
throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_METHOD_INVOCATION, name,
state.getActiveContextObject().getClass().getName(), ae.getMessage());
}
}
private Class<?>[] getTypes(Object... arguments) {
if (arguments == null)
return null;
Class<?>[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentTypes[i] = arguments[i].getClass();
}
return argumentTypes;
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append(name).append("(");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(",");
sb.append(getChild(i).toStringAST());
}
sb.append(")");
return sb.toString();
}
/**
* Produce a nice string for a given method name with specified arguments.
* @param name the name of the method
* @param argumentTypes the types of the arguments to the method
* @return nicely formatted string, eg. foo(String,int)
*/
private String formatMethodForMessage(String name, Class<?>... argumentTypes) {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append("(");
if (argumentTypes != null) {
for (int i = 0; i < argumentTypes.length; i++) {
if (i > 0)
sb.append(",");
sb.append(argumentTypes[i].getClass());
}
}
sb.append(")");
return sb.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
public final MethodExecutor findAccessorForMethod(String name, Class<?>[] argumentTypes, ExpressionState state)
throws SpelException {
Object contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
if (contextObject == null) {
throw new SpelException(SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT, Utils
.formatMethodForMessage(name, argumentTypes));
}
List<MethodResolver> mResolvers = eContext.getMethodResolvers();
if (mResolvers != null) {
for (MethodResolver methodResolver : mResolvers) {
try {
MethodExecutor cEx = methodResolver.resolve(state.getEvaluationContext(), contextObject, name,
argumentTypes);
if (cEx != null)
return cEx;
} catch (AccessException e) {
Throwable cause = e.getCause();
if (cause instanceof SpelException) {
throw (SpelException) cause;
} else {
throw new SpelException(cause, SpelMessages.PROBLEM_LOCATING_METHOD, name, contextObject.getClass());
}
}
}
}
throw new SpelException(SpelMessages.METHOD_NOT_FOUND, Utils.formatMethodForMessage(name, argumentTypes), Utils
.formatClassnameForMessage(contextObject instanceof Class ? ((Class<?>) contextObject) : contextObject
.getClass()));
// (contextObject instanceof Class ? ((Class<?>) contextObject).getName() : contextObject.getClass()
// .getName()));
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
public class NullLiteral extends Literal {
public NullLiteral(Token payload) {
super(payload);
}
@Override
public String getLiteralValue() {
return null;
}
@Override
public String toString() {
return null;
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Common supertype for operators that operate on either one or two operands. In the case of multiply or divide there
* would be two operands, but for unary plus or minus, there is only one.
*
* @author Andy Clement
*/
public abstract class Operator extends SpelNode {
public Operator(Token payload) {
super(payload);
}
/**
* Operator expressions can never be written to
*/
@Override
public final boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
public SpelNode getLeftOperand() {
return getChild(0);
}
public SpelNode getRightOperand() {
return getChild(1);
}
public abstract String getOperatorName();
/**
* String format for all operators is the same '(' [operand] [operator] [operand] ')'
*/
@Override
public String toStringAST() {
StringBuffer sb = new StringBuffer();
if (getChildCount() > 0)
sb.append("(");
sb.append(getChild(0).toStringAST());
for (int i = 1; i < getChildCount(); i++) {
sb.append(" ").append(getOperatorName()).append(" ");
sb.append(getChild(i).toStringAST());
}
if (getChildCount() > 0)
sb.append(")");
return sb.toString();
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents the boolean AND operation.
*
* @author Andy Clement
*/
public class OperatorAnd extends Operator {
public OperatorAnd(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "and";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
boolean leftValue;
boolean rightValue;
try {
leftValue = state.toBoolean(getLeftOperand().getValue(state));
} catch (SpelException ee) {
ee.setPosition(getLeftOperand().getCharPositionInLine());
throw ee;
}
if (leftValue == false) {
return false; // no need to evaluate right operand
}
try {
rightValue = state.toBoolean(getRightOperand().getValue(state));
} catch (SpelException ee) {
ee.setPosition(getRightOperand().getCharPositionInLine());
throw ee;
}
return /* leftValue && */rightValue;
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents the between operator. The left operand to between must be a single value and the right operand must be a
* list - this operator returns true if the left operand is between (using the registered comparator) the two elements
* in the list.
*
* @author Andy Clement
*/
public class OperatorBetween extends Operator {
public OperatorBetween(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "between";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (!(right instanceof List) || ((List<?>) right).size() != 2) {
throw new SpelException(getRightOperand().getCharPositionInLine(),
SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST);
}
List<?> l = (List<?>) right;
Object low = l.get(0);
Object high = l.get(1);
TypeComparator comparator = state.getTypeComparator();
try {
// TODO between is inclusive, is that OK
return (comparator.compare(left, low) >= 0 && comparator.compare(left, high) <= 0);
} catch (SpelException ee) {
ee.setPosition(getCharPositionInLine());
throw ee;
}
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
// TODO is the operator 'distanceto' any use...?
/**
* The distanceto operator uses an implementation of the levenshtein distance measurement for determining the 'edit
* distance' between two strings (the two operands to distanceto). http://en.wikipedia.org/wiki/Levenshtein_distance
* @author Andy Clement
*
*/
public class OperatorDistanceTo extends Operator {
private final static boolean DEBUG = false;
public OperatorDistanceTo(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "distanceto";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
try {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
return computeDistanceTo((String) left, (String) right);
} catch (SpelException ee) {
throw ee;
}
}
private int computeDistanceTo(String from, String to) {
if (from.equals(to))
return 0;
int[][] d = new int[from.length() + 1][to.length() + 1];
for (int i = 0; i <= from.length(); i++)
d[i][0] = i;
for (int j = 0; j <= to.length(); j++)
d[0][j] = j;
for (int i = 1; i <= from.length(); i++) {
for (int j = 1; j <= to.length(); j++) {
int cost;
if (from.charAt(i - 1) == to.charAt(j - 1))
cost = 0;
else
cost = 1;
d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);// del,ins,subst
}
}
if (DEBUG) {
// Display the table of values
System.out.print(" ");
for (int j = 0; j < from.length(); j++) {
System.out.print(from.charAt(j) + " ");
}
System.out.println();
for (int j = 0; j < to.length() + 1; j++) {
System.out.print((j > 0 ? to.charAt(j - 1) : " ") + " ");
for (int i = 0; i < from.length() + 1; i++) {
System.out.print(d[i][j]);
if (i == from.length() && j == to.length())
System.out.print("<");
else if (i == from.length() - 1 && j == to.length())
System.out.print(">");
else
System.out.print(" ");
}
System.out.println();
}
}
return d[from.length()][to.length()];
}
private int min(int i, int j, int k) {
int min = i;
if (j < min)
min = j;
if (k < min)
min = k;
return min;
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements division operator
*
* @author Andy Clement
*/
public class OperatorDivide extends Operator {
public OperatorDivide(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "/";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValue(state);
Object operandTwo = getRightOperand().getValue(state);
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
Double result = op1.doubleValue() / op2.doubleValue();
return result;
} else if (op1 instanceof Float || op2 instanceof Float) {
Float result = op1.floatValue() / op2.floatValue();
return result;
} else if (op1 instanceof Long || op2 instanceof Long) {
Long result = op1.longValue() / op2.longValue();
return result;
} else { // TODO what about non-int result of the division?
Integer result = op1.intValue() / op2.intValue();
return result;
}
}
return state.operate(Operation.DIVIDE, operandOne, operandTwo);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements equality operator.
*
* @author Andy Clement
*/
public class OperatorEquality extends Operator {
public OperatorEquality(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "==";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() == op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() == op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() == op2.longValue();
} else {
return op1.intValue() == op2.intValue();
}
}
return state.getTypeComparator().compare(left, right) == 0;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements greater than operator.
*
* @author Andy Clement
*/
public class OperatorGreaterThan extends Operator {
public OperatorGreaterThan(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return ">";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() > op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() > op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() > op2.longValue();
} else {
return op1.intValue() > op2.intValue();
}
}
return state.getTypeComparator().compare(left, right) > 0;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements greater than or equal operator.
*
* @author Andy Clement
*/
public class OperatorGreaterThanOrEqual extends Operator {
public OperatorGreaterThanOrEqual(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return ">=";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() >= op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() >= op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() >= op2.longValue();
} else {
return op1.intValue() >= op2.intValue();
}
}
return state.getTypeComparator().compare(left, right) >= 0;
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.Collection;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents the 'in' operator and returns true if the left operand can be found within the collection passed as the
* right operand.
*
* @author Andy Clement
*/
public class OperatorIn extends Operator {
public OperatorIn(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "in";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (right instanceof Collection<?>) {
Collection<?> c = (Collection<?>) right;
for (Object element : c) {
if (state.getTypeComparator().compare(left, element) == 0) {
return true;
}
}
return false;
}
throw new SpelException(SpelMessages.OPERATOR_IN_CANNOT_DETERMINE_MEMBERSHIP, left.getClass().getName(), right
.getClass().getName());
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the not-equal operator
*
* @author Andy Clement
*/
public class OperatorInequality extends Operator {
public OperatorInequality(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "!=";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() != op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() != op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() != op2.longValue();
} else {
return op1.intValue() != op2.intValue();
}
}
return state.getTypeComparator().compare(left, right) != 0;
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* The operator 'is' checks if an object is of the class specified in the right hand operand, in the same way that
* instanceof does in Java.
*
* @author Andy Clement
*
*/
public class OperatorIs extends Operator {
// TODO should 'is' change to 'instanceof' ?
public OperatorIs(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "is";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (!(right instanceof Class<?>)) {
throw new SpelException(getRightOperand().getCharPositionInLine(),
SpelMessages.IS_OPERATOR_NEEDS_CLASS_OPERAND, right.getClass().getName());
}
// TODO Could this defer to type utilities? What would be the benefit?
return (((Class<?>) right).isAssignableFrom(left.getClass()));
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the less than operator
*
* @author Andy Clement
*/
public class OperatorLessThan extends Operator {
public OperatorLessThan(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "<";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() < op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() < op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() < op2.longValue();
} else {
return op1.intValue() < op2.intValue();
}
}
return state.getTypeComparator().compare(left, right) < 0;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the less than or equal operator
*
* @author Andy Clement
*/
public class OperatorLessThanOrEqual extends Operator {
public OperatorLessThanOrEqual(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() <= op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() <= op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() <= op2.longValue();
} else {
return op1.intValue() <= op2.intValue();
}
}
return state.getTypeComparator().compare(left, right) <= 0;
}
@Override
public String getOperatorName() {
return "<=";
}
}

View File

@ -0,0 +1,68 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the like operator. The expected operands for like are a string and a pattern (JDK regex). The operator
* will return true if the string matches the regex.
*
* @author Andy Clement
*/
public class OperatorLike extends Operator {
public OperatorLike(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "like";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
SpelNode leftOp = getLeftOperand();
SpelNode rightOp = getRightOperand();
Object left = leftOp.getValue(state);
Object right = getRightOperand().getValue(state);
try {
if (!(left instanceof String)) {
throw new SpelException(leftOp.getCharPositionInLine(),
SpelMessages.INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR, left);
}
if (!(right instanceof String)) {
throw new SpelException(rightOp.getCharPositionInLine(),
SpelMessages.INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR, right);
}
Pattern pattern = Pattern.compile((String) right);
Matcher matcher = pattern.matcher((String) left);
return matcher.matches();
} catch (PatternSyntaxException pse) {
throw new SpelException(rightOp.getCharPositionInLine(), pse, SpelMessages.INVALID_PATTERN, right);
}
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
// TODO what should be the difference between like and matches?
/**
* Implements the matches operator.
*
* @author Andy Clement
*/
public class OperatorMatches extends Operator {
public OperatorMatches(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
try {
Pattern pattern = Pattern.compile((String) right);
Matcher matcher = pattern.matcher((String) left);
return matcher.matches();
} catch (PatternSyntaxException pse) {
throw new SpelException(pse, SpelMessages.INVALID_PATTERN, right);
}
}
@Override
public String getOperatorName() {
return "matches";
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the minus operator. If there is only one operand it is a unary minus.
*
* @author Andy Clement
*/
public class OperatorMinus extends Operator {
public OperatorMinus(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "-";
}
@Override
public String toStringAST() {
if (getRightOperand() == null) { // unary minus
return new StringBuilder().append("-").append(getLeftOperand()).toString();
}
return super.toStringAST();
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
SpelNode leftOp = getLeftOperand();
SpelNode rightOp = getRightOperand();
if (rightOp == null) {// If only one operand, then this is unary minus
Object left = leftOp.getValue(state);
if (left instanceof Number) {
Number n = (Number) left;
if (left instanceof Double) {
Double result = 0 - n.doubleValue();
return result;
} else if (left instanceof Float) {
Float result = 0 - n.floatValue();
return result;
} else if (left instanceof Long) {
Long result = 0 - n.longValue();
return result;
} else {
Integer result = 0 - n.intValue();
return result;
}
}
throw new SpelException(SpelMessages.CANNOT_NEGATE_TYPE, left.getClass().getName());
} else {
Object left = leftOp.getValue(state);
Object right = rightOp.getValue(state);
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
Double result = op1.doubleValue() - op2.doubleValue();
return result;
} else if (op1 instanceof Float || op2 instanceof Float) {
Float result = op1.floatValue() - op2.floatValue();
return result;
} else if (op1 instanceof Long || op2 instanceof Long) {
Long result = op1.longValue() - op2.longValue();
return result;
} else {
Integer result = op1.intValue() - op2.intValue();
return result;
}
}
return state.operate(Operation.SUBTRACT, left, right);
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the modulus operator.
*
* @author Andy Clement
*/
public class OperatorModulus extends Operator {
public OperatorModulus(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "%";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValue(state);
Object operandTwo = getRightOperand().getValue(state);
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
Double result = op1.doubleValue() % op2.doubleValue();
return result;
} else if (op1 instanceof Float || op2 instanceof Float) {
Float result = op1.floatValue() % op2.floatValue();
return result;
} else if (op1 instanceof Long || op2 instanceof Long) {
Long result = op1.longValue() % op2.longValue();
return result;
} else {
Integer result = op1.intValue() % op2.intValue();
return result;
}
}
return state.operate(Operation.MODULUS, operandOne, operandTwo);
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the multiply operator. Conversions and promotions:
* http://java.sun.com/docs/books/jls/third_edition/html/conversions.html Section 5.6.2:
*
* If any of the operands is of a reference type, unboxing conversion (¤5.1.8) is performed. Then:<br>
* If either operand is of type double, the other is converted to double.<br>
* Otherwise, if either operand is of type float, the other is converted to float.<br>
* Otherwise, if either operand is of type long, the other is converted to long.<br>
* Otherwise, both operands are converted to type int.
*
* @author Andy Clement
*/
public class OperatorMultiply extends Operator {
public OperatorMultiply(Token payload) {
super(payload);
}
/**
* Implements multiply directly here for some types of operand, otherwise delegates to any registered overloader for
* types it does not recognize. Supported types here are:
* <ul>
* <li>integers
* <li>doubles
* <li>string and int ('abc' * 2 == 'abcabc')
* </ul>
*/
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
// TODO could have an 'int only' arithmetic mode for super fast expression evaluation
Object operandOne = getLeftOperand().getValue(state);
Object operandTwo = getRightOperand().getValue(state);
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
Double result = op1.doubleValue() * op2.doubleValue();
return result;
} else if (op1 instanceof Float || op2 instanceof Float) {
Float result = op1.floatValue() * op2.floatValue();
return result;
} else if (op1 instanceof Long || op2 instanceof Long) {
Long result = op1.longValue() * op2.longValue();
return result;
} else { // promote to int
Integer result = op1.intValue() * op2.intValue();
return result;
}
} else if (operandOne instanceof String && operandTwo instanceof Integer) {
int repeats = ((Integer) operandTwo).intValue();
StringBuilder result = new StringBuilder();
for (int i = 0; i < repeats; i++) {
result.append(operandOne);
}
return result.toString();
}
return state.operate(Operation.MULTIPLY, operandOne, operandTwo);
}
@Override
public String getOperatorName() {
return "*";
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
public class OperatorNot extends SpelNode { // Not is a unary operator so do not extend BinaryOperator
public OperatorNot(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
try {
boolean value = state.toBoolean(getChild(0).getValue(state));
return !value;
} catch (SpelException see) {
see.setPosition(getChild(0).getCharPositionInLine());
throw see;
}
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("!").append(getChild(0).toStringAST());
return sb.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents the boolean OR operation.
*
* @author Andy Clement
*/
public class OperatorOr extends Operator {
public OperatorOr(Token payload) {
super(payload);
}
@Override
public String getOperatorName() {
return "or";
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
boolean leftValue;
boolean rightValue;
try {
leftValue = state.toBoolean(getLeftOperand().getValue(state));
} catch (SpelException see) {
see.setPosition(getLeftOperand().getCharPositionInLine());
throw see;
}
if (leftValue == true)
return true; // no need to evaluate right operand
try {
rightValue = state.toBoolean(getRightOperand().getValue(state));
} catch (SpelException see) {
see.setPosition(getRightOperand().getCharPositionInLine());
throw see;
}
return leftValue || rightValue;
}
}

View File

@ -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.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Operation;
import org.springframework.expression.spel.ExpressionState;
public class OperatorPlus extends Operator {
public OperatorPlus(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
SpelNode leftOp = getLeftOperand();
SpelNode rightOp = getRightOperand();
if (rightOp == null) { // If only one operand, then this is unary plus
Object operandOne = leftOp.getValue(state);
if (operandOne instanceof Number) {
return new Integer(((Number) operandOne).intValue());
}
return state.operate(Operation.ADD, operandOne, null);
} else {
Object operandOne = leftOp.getValue(state);
Object operandTwo = rightOp.getValue(state);
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
Double result = op1.doubleValue() + op2.doubleValue();
return result;
} else if (op1 instanceof Float || op2 instanceof Float) {
Float result = op1.floatValue() + op2.floatValue();
return result;
} else if (op1 instanceof Long || op2 instanceof Long) {
Long result = op1.longValue() + op2.longValue();
return result;
} else { // TODO what about overflow?
Integer result = op1.intValue() + op2.intValue();
return result;
}
} else if (operandOne instanceof String && operandTwo instanceof String) {
return new StringBuilder((String) operandOne).append((String) operandTwo).toString();
} else if (operandOne instanceof String && operandTwo instanceof Integer) {
String l = (String) operandOne;
Integer i = (Integer) operandTwo;
// implements character + int (ie. a + 1 = b)
if (l.length() == 1) {
Character c = new Character((char) (new Character(l.charAt(0)) + i));
return c.toString();
}
return new StringBuilder((String) operandOne).append(((Integer) operandTwo).toString()).toString();
}
return state.operate(Operation.ADD, operandOne, operandTwo);
}
}
@Override
public String getOperatorName() {
return "+";
}
@Override
public String toStringAST() {
if (getRightOperand() == null) { // unary plus
return new StringBuilder().append("+").append(getLeftOperand()).toString();
}
return super.toStringAST();
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
public class OperatorSoundsLike extends Operator {
public OperatorSoundsLike(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValue(state);
Object right = getRightOperand().getValue(state);
if (!(left instanceof String)) {
throw new SpelException(getCharPositionInLine(), SpelMessages.SOUNDSLIKE_NEEDS_STRING_OPERAND, left.getClass()
.getName());
}
if (!(right instanceof String)) {
throw new SpelException(getCharPositionInLine(), SpelMessages.SOUNDSLIKE_NEEDS_STRING_OPERAND, right.getClass()
.getName());
}
String leftSoundex = computeSoundex((String) left);
String rightSoundex = computeSoundex((String) right);
return state.getTypeComparator().compare(leftSoundex, rightSoundex) == 0;
}
// TODO OPTIMIZE better algorithm implementation is possible for soundex
private String computeSoundex(String input) {
if (input == null || input.length() == 0)
return "0000";
input = input.toUpperCase();
StringBuilder soundex = new StringBuilder();
soundex.append(input.charAt(0));
for (int i = 1; i < input.length(); i++) {
char ch = input.charAt(i);
if ("HW".indexOf(ch) != -1)
continue; // remove HWs now
if ("BFPV".indexOf(ch) != -1) {
soundex.append("1");
} else if ("CGJKQSXZ".indexOf(ch) != -1) {
soundex.append("2");
} else if ("DT".indexOf(ch) != -1) {
soundex.append("3");
} else if ("L".indexOf(ch) != -1) {
soundex.append("4");
} else if ("MN".indexOf(ch) != -1) {
soundex.append("5");
} else if ("R".indexOf(ch) != -1) {
soundex.append("6");
} else {
soundex.append(ch);
}
}
StringBuilder shorterSoundex = new StringBuilder();
shorterSoundex.append(soundex.charAt(0));
for (int i = 1; i < soundex.length(); i++) {
if ((i + 1) < soundex.length() && soundex.charAt(i) == soundex.charAt(i + 1))
continue;
if ("AEIOUY".indexOf(soundex.charAt(i)) != -1)
continue;
shorterSoundex.append(soundex.charAt(i));
}
shorterSoundex.append("0000");
return shorterSoundex.substring(0, 4);
}
// wikipedia:
// The Soundex code for a name consists of a letter followed by three numbers: the letter is the first letter of the
// name, and the numbers encode the remaining consonants. Similar sounding consonants share the same number so, for
// example, the labial B, F, P and V are all encoded as 1. Vowels can affect the coding, but are never coded
// directly unless they appear at the start of the name.
// The exact algorithm is as follows:
// Retain the first letter of the string
// Remove all occurrences of the following letters, unless it is the first letter: a, e, h, i, o, u, w, y
// Assign numbers to the remaining letters (after the first) as follows:
// b, f, p, v = 1
// c, g, j, k, q, s, x, z = 2
// d, t = 3
// l = 4
// m, n = 5
// r = 6
// If two or more letters with the same number were adjacent in the original name (before step 1), or adjacent
// except for any intervening h and w (American census only), then omit all but the first.
// Return the first four characters, right-padding with zeroes if there are fewer than four.
// Using this algorithm, both "Robert" and "Rupert" return the same string "R163" while "Rubin" yields "R150".
@Override
public String getOperatorName() {
return "soundslike";
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* PlaceHolder nodes are created for tokens that come through from the grammar purely to give us additional positional
* information for messages/etc.
*
* @author Andy Clement
*
*/
public class Placeholder extends SpelNode {
public Placeholder(Token payload) {
super(payload);
}
@Override
public String getValue(ExpressionState state) throws SpelException {
throw new SpelException(getCharPositionInLine(), SpelMessages.PLACEHOLDER_SHOULD_NEVER_BE_EVALUATED);
}
@Override
public String toStringAST() {
return getText();
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.internal.KeyValuePair;
/**
* Represents projection, where a given operation is performed on all elements in some input sequence, returning
* a new sequence of the same size. For example:
* "{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}" returns "[n, y, n, y, n, y, n, y, n, y]"
*
* @author Andy Clement
*
*/
public class Projection extends SpelNode {
public Projection(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object operand = state.getActiveContextObject();
// When the input is a map, we push a special context object on the stack
// before calling the specified operation. This special context object
// has two fields 'key' and 'value' that refer to the map entries key
// and value, and they can be referenced in the operation
// eg. {'a':'y','b':'n'}.!{value=='y'?key:null}" == ['a', null]
if (operand instanceof Map) {
Map<?, ?> mapdata = (Map<?, ?>) operand;
List<Object> result = new ArrayList<Object>();
for (Object k : mapdata.keySet()) {
try {
state.pushActiveContextObject(new KeyValuePair(k, mapdata.get(k)));
result.add(getChild(0).getValue(state));
} finally {
state.popActiveContextObject();
}
}
return result;
} else if (operand instanceof Collection) {
List<Object> data = new ArrayList<Object>();
data.addAll((Collection<?>) operand);
List<Object> result = new ArrayList<Object>();
int idx = 0;
for (Object element : data) {
try {
state.pushActiveContextObject(element);
state.enterScope("index", idx);
result.add(getChild(0).getValue(state));
} finally {
state.exitScope();
state.popActiveContextObject();
}
idx++;
}
return result;
} else {
throw new SpelException(SpelMessages.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
}
}
@Override
public String toStringAST() {
StringBuffer sb = new StringBuffer();
return sb.append("!{").append(getChild(0).toStringAST()).append("}").toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,239 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
import java.util.List;
import org.antlr.runtime.Token;
import org.springframework.expression.AccessException;
import org.springframework.expression.CacheablePropertyAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.PropertyReaderExecutor;
import org.springframework.expression.PropertyWriterExecutor;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.internal.Utils;
/**
* Represents a simple property or field reference.
*
* @author Andy Clement
*/
public class PropertyOrFieldReference extends SpelNode {
public static boolean useCaching = true;
private final Object name;
private PropertyReaderExecutor cachedReaderExecutor;
private PropertyWriterExecutor cachedWriterExecutor;
public PropertyOrFieldReference(Token payload) {
super(payload);
name = payload.getText();
}
@Override
public Object getValue(ExpressionState state) throws SpelException {
return readProperty(state, name);
}
@Override
public void setValue(ExpressionState state, Object newValue) throws SpelException {
writeProperty(state, name, newValue);
}
@Override
public boolean isWritable(ExpressionState state) throws SpelException {
return isWritableProperty(name, state);
}
@Override
public String toStringAST() {
return name.toString();
}
/**
* Attempt to read the named property from the current context object.
*
* @param state the evaluation state
* @param name the name of the property
* @return the value of the property
* @throws SpelException if any problem accessing the property or it cannot be found
*/
private Object readProperty(ExpressionState state, Object name) throws SpelException {
Object contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
if (cachedReaderExecutor != null) {
try {
return cachedReaderExecutor.execute(state.getEvaluationContext(), contextObject);
} catch (AccessException ae) {
// this is OK - it may have gone stale due to a class change,
// let's try to get a new one and call it before giving up
}
}
Class<?> contextObjectClass = getObjectClass(contextObject);
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
// get the accessor and use it. If they are not cacheable but report they can read the property
// then ask them to read it
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor instanceof CacheablePropertyAccessor) {
cachedReaderExecutor = ((CacheablePropertyAccessor)accessor).getReaderAccessor(eContext, contextObject, name);
if (cachedReaderExecutor != null) {
try {
return cachedReaderExecutor.execute(state.getEvaluationContext(), contextObject);
} catch (AccessException ae) {
cachedReaderExecutor = null;
throw ae;
} finally {
if (!useCaching) {
cachedReaderExecutor = null;
}
}
}
} else {
if (accessor.canRead(eContext, contextObject, name)) {
Object value = accessor.read(eContext, contextObject, name);
return value;
}
}
}
} catch (AccessException ae) {
throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage());
}
}
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, name, Utils.formatClassnameForMessage(contextObjectClass));
}
private void writeProperty(ExpressionState state, Object name, Object newValue) throws SpelException {
Object contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
if (cachedWriterExecutor != null) {
try {
cachedWriterExecutor.execute(state.getEvaluationContext(), contextObject, newValue);
return;
} catch (AccessException ae) {
// this is OK - it may have gone stale due to a class change,
// let's try to get a new one and call it before giving up
}
}
Class<?> contextObjectClass = getObjectClass(contextObject);
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
if (accessorsToTry != null) {
try {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor instanceof CacheablePropertyAccessor) {
cachedWriterExecutor = ((CacheablePropertyAccessor)accessor).getWriterAccessor(eContext, contextObject, name);
if (cachedWriterExecutor != null) {
try {
cachedWriterExecutor.execute(state.getEvaluationContext(), contextObject, newValue);
return;
} catch (AccessException ae) {
cachedWriterExecutor = null;
throw ae;
} finally {
if (!useCaching) {
cachedWriterExecutor = null;
}
}
}
} else {
if (accessor.canWrite(eContext, contextObject, name)) {
accessor.write(eContext, contextObject, name, newValue);
return;
}
}
}
} catch (AccessException ae) {
throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_PROPERTY_WRITE, name, ae.getMessage());
}
}
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND, name, Utils
.formatClassnameForMessage(contextObjectClass));
}
public boolean isWritableProperty(Object name, ExpressionState state) throws SpelException {
Object contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
if (contextObject == null) {
throw new SpelException(SpelMessages.ATTEMPTED_PROPERTY_FIELD_REF_ON_NULL_CONTEXT_OBJECT, name);
}
List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(
(contextObject instanceof Class) ? ((Class<?>) contextObject) : contextObject.getClass(), state);
if (resolversToTry != null) {
for (PropertyAccessor pfResolver : resolversToTry) {
try {
if (pfResolver.canWrite(eContext, contextObject, name)) {
return true;
}
} catch (AccessException ae) {
// let others try
}
}
}
return false;
}
/**
* Determines the set of property resolvers that should be used to try and access a property on the specified target
* type. The resolvers are considered to be in an ordered list, however in the returned list any that are exact
* matches for the input target type (as opposed to 'general' resolvers that could work for any type) are placed at
* the start of the list. In addition, there are specific resolvers that exactly name the class in question and
* resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the
* specific resolvers set and will be tried after exactly matching accessors but before generic accessors.
*
* @param targetType the type upon which property access is being attempted
* @return a list of resolvers that should be tried in order to access the property
*/
private List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, ExpressionState state) {
List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
for (PropertyAccessor resolver : state.getPropertyAccessors()) {
Class<?>[] targets = resolver.getSpecificTargetClasses();
if (targets == null) { // generic resolver that says it can be used for any type
generalAccessors.add(resolver);
} else {
int pos = 0;
for (int i = 0; i < targets.length; i++) {
Class<?> clazz = targets[i];
if (clazz == targetType) { // put exact matches on the front to be tried first?
specificAccessors.add(pos++, resolver);
} else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the specificAccessor list
generalAccessors.add(resolver);
}
}
}
}
List<PropertyAccessor> resolvers = new ArrayList<PropertyAccessor>();
resolvers.addAll(specificAccessors);
resolvers.addAll(generalAccessors);
return resolvers;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a dot separated sequence of strings that indicate a package qualified type reference.
* <p>
* Example: "java.lang.String" as in the expression "new java.lang.String('hello')"
*
* @author Andy Clement
*
*/
public class QualifiedIdentifier extends SpelNode {
private String value;
public QualifiedIdentifier(Token payload) {
super(payload);
// value = payload.getText();
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
// Cache the concatenation of child identifiers
if (value == null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(".");
sb.append(getChild(i).getValue(state));
}
value = sb.toString();
}
return value;
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
if (value != null) {
sb.append(value);
} else {
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
sb.append(".");
sb.append(getChild(i).toStringAST());
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
public class RealLiteral extends Literal {
private final Double value;
public RealLiteral(Token payload) {
super(payload);
value = Double.parseDouble(payload.getText());
}
@Override
public Double getLiteralValue() {
return value;
}
}

View File

@ -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.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.generated.SpringExpressionsLexer;
/**
* Represent a object reference of the form '@(<contextName>:<objectName>)'
*
*/
public class Reference extends SpelNode {
private boolean contextAndObjectDetermined = false;
private SpelNode contextNode = null;
private SpelNode objectNode = null;
public Reference(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
ensureContextAndNameDetermined();
Object contextName = (contextNode == null ? null : contextNode.getValue(state));
Object objectName = (objectNode == null ? null : objectNode.getValue(state));
Object referencedValue = state.lookupReference(contextName, objectName);
return referencedValue;
}
/**
* Work out which represents the context and which the object. This would be trivial except for parser recovery
* situations where the expression was incomplete. We need to do our best here to recover so that we can offer
* suitable code completion suggestions.
*/
private void ensureContextAndNameDetermined() {
if (contextAndObjectDetermined)
return;
contextAndObjectDetermined = true;
int colon = -1;
for (int i = 0; i < getChildCount(); i++) {
if (getChild(i).getToken().getType() == SpringExpressionsLexer.COLON) {
colon = i;
}
}
if (colon != -1) {
contextNode = getChild(colon - 1);
objectNode = getChild(colon + 1);
} else {
objectNode = getChild(0);
}
if (objectNode.getToken().getType() != SpringExpressionsLexer.QUALIFIED_IDENTIFIER) {
objectNode = null;
}
}
@Override
public String toStringAST() {
ensureContextAndNameDetermined();
StringBuilder sb = new StringBuilder();
sb.append("@(");
if (contextNode != null) {
sb.append(contextNode.toStringAST()).append(":");
}
sb.append(objectNode.toStringAST());
sb.append(")");
return sb.toString();
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.internal.KeyValuePair;
/**
* Represents selection over a map or collection. For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns
* [2, 4, 6, 8, 10]
*
* Basically a subset of the input data is returned based on the evaluation of the expression supplied as selection
* criteria.
*
* @author Andy Clement
*/
public class Selection extends SpelNode {
public final static int ALL = 0; // ?{}
public final static int FIRST = 1; // ^{}
public final static int LAST = 2; // ${}
private final int variant;
public Selection(Token payload, int variant) {
super(payload);
this.variant = variant;
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object operand = state.getActiveContextObject();
SpelNode selectionCriteria = getChild(0);
if (operand instanceof Map) {
Map<?, ?> mapdata = (Map<?, ?>) operand;
List<Object> result = new ArrayList<Object>();
for (Object k : mapdata.keySet()) {
try {
Object kvpair = new KeyValuePair(k, mapdata.get(k));
state.pushActiveContextObject(kvpair);
Object o = selectionCriteria.getValue(state);
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
if (variant == FIRST)
return kvpair;
result.add(kvpair);
}
} else {
throw new SpelException(selectionCriteria.getCharPositionInLine(),
SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
}
} finally {
state.popActiveContextObject();
}
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
return null;
}
if (variant == LAST) {
return result.get(result.size() - 1);
}
}
return result;
} else if (operand instanceof Collection) {
List<Object> data = new ArrayList<Object>();
data.addAll((Collection<?>) operand);
List<Object> result = new ArrayList<Object>();
int idx = 0;
for (Object element : data) {
try {
state.pushActiveContextObject(element);
state.enterScope("index", idx);
Object o = selectionCriteria.getValue(state);
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
if (variant == FIRST)
return element;
result.add(element);
}
} else {
throw new SpelException(selectionCriteria.getCharPositionInLine(),
SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
}
idx++;
} finally {
state.exitScope();
state.popActiveContextObject();
}
}
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
return null;
}
if (variant == LAST) {
return result.get(result.size() - 1);
}
return result;
} else {
throw new SpelException(getCharPositionInLine(), SpelMessages.INVALID_TYPE_FOR_SELECTION,
(operand == null ? "null" : operand.getClass().getName()));
}
}
@Override
public String toStringAST() {
StringBuffer sb = new StringBuffer();
switch (variant) {
case ALL:
sb.append("?{");
break;
case FIRST:
sb.append("^{");
break;
case LAST:
sb.append("${");
break;
}
return sb.append(getChild(0).toStringAST()).append("}").toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.io.Serializable;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.generated.SpringExpressionsParser;
/**
* The common supertype of all AST nodes in a parsed Spring Expression Language format expression.
*
* @author Andy Clement
*
*/
public abstract class SpelNode extends CommonTree implements Serializable {
/**
* The Antlr parser uses this constructor to build SpelNodes.
*
* @param payload the token for the node that has been parsed
*/
protected SpelNode(Token payload) {
super(payload);
}
/**
* Evaluate the expression node in the context of the supplied expression state and return the value.
*
* @param expressionState the current expression state (includes the context)
* @return the value of this node evaluated against the specified state
*/
public abstract Object getValue(ExpressionState expressionState) throws EvaluationException;
/**
* Determine if this expression node will support a setValue() call.
*
* @param expressionState the current expression state (includes the context)
* @return true if the expression node will allow setValue()
* @throws EvaluationException if something went wrong trying to determine if the node supports writing
*/
public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
return false;
}
/**
* Evaluate the expression to a node and then set the new value on that node. For example, if the expression
* evaluates to a property reference then the property will be set to the new value.
*
* @param expressionState the current expression state (includes the context)
* @param newValue the new value
* @throws EvaluationException if any problem occurs evaluating the expression or setting the new value
*/
public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
throw new SpelException(getCharPositionInLine(), SpelMessages.SETVALUE_NOT_SUPPORTED, getClass(), getTokenName());
}
/**
* @return return the token this node represents
*/
protected String getTokenName() {
if (getToken() == null) {
return "UNKNOWN";
}
return SpringExpressionsParser.tokenNames[getToken().getType()];
}
/**
* @return the string form of this AST node
*/
public abstract String toStringAST();
/**
* Helper method that returns a SpelNode rather than an Antlr Tree node.
*
* @return the child node cast to a SpelNode
*/
@Override
public SpelNode getChild(int index) {
return (SpelNode) super.getChild(index);
}
/**
* Determine the class of the object passed in, unless it is already a class object.
* @param o the object that the caller wants the class of
* @return the class of the object if it is not already a class object, or null if the object is null
*/
public Class<?> getObjectClass(Object o) {
if (o==null) return null;
return (o instanceof Class) ? ((Class<?>) o) : o.getClass();
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
public class StringLiteral extends Literal {
private String value;
public StringLiteral(Token payload) {
super(payload);
value = payload.getText();
// TODO should these have been skipped being created by the parser rules? or not?
value = value.substring(1, value.length() - 1);
value = value.replaceAll("''", "'");
}
@Override
public String getLiteralValue() {
return value;
}
@Override
public String toString() {
return new StringBuilder("'").append(getLiteralValue()).append("'").toString();
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a ternary expression, for example: "someCheck()?true:false".
*
* @author Andy Clement
*/
public class Ternary extends SpelNode {
public Ternary(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
Object condition = getChild(0).getValue(state);
try {
boolean b = state.toBoolean(condition);
if (b)
return getChild(1).getValue(state);
else
return getChild(2).getValue(state);
} catch (SpelException see) {
see.setPosition(getChild(0).getCharPositionInLine());
throw see;
}
}
@Override
public String toStringAST() {
return new StringBuilder().append(getChild(0).toStringAST()).append(" ? ").append(getChild(1).toStringAST())
.append(" : ").append(getChild(2).toStringAST()).toString();
}
// TODO 3 should this say TRUE if the left or the right are writable???
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.internal.TypeCode;
/**
* Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)"
*
* @author Andy Clement
*
*/
public class TypeReference extends SpelNode {
public TypeReference(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws EvaluationException {
// TODO OPTIMIZE cache the type reference once found?
String typename = (String) getChild(0).getValue(state);
if (typename.indexOf(".") == -1 && Character.isLowerCase(typename.charAt(0))) {
TypeCode tc = TypeCode.forName(typename);
if (tc != TypeCode.OBJECT) {
// it is a primitive type
return tc.getType();
}
}
return state.findType(typename);
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("T(");
sb.append(getChild(0).toStringAST());
sb.append(")");
return sb.toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a variable reference, eg. #someVar. Note this is different to a *local* variable like $someVar
*
* @author Andy Clement
*
*/
public class VariableReference extends SpelNode {
// Well known variables:
private final static String THIS = "this"; // currently active context object
private final static String ROOT = "root"; // root context object
private final String name;
public VariableReference(Token payload) {
super(payload);
name = payload.getText();
}
@Override
public Object getValue(ExpressionState state) throws SpelException {
if (name.equals(THIS))
return state.getActiveContextObject();
if (name.equals(ROOT))
return state.getRootContextObject();
Object result = state.lookupVariable(name);
if (result == null) {
throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, name);
}
return result;
}
@Override
public void setValue(ExpressionState state, Object value) throws SpelException {
// Object oldValue = state.lookupVariable(name);
state.setVariable(name, value);
}
@Override
public String toStringAST() {
return new StringBuilder("#").append(name).toString();
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return !(name.equals(THIS) || name.equals(ROOT));
}
}

View File

@ -0,0 +1,375 @@
grammar SpringExpressions;
options {
language = Java;
output=AST;
k=2;
//caseSensitive = false;
//backtrack=true;
}
tokens {
EXPRESSIONLIST;
INTEGER_LITERAL;
EXPRESSION;
QUALIFIED_IDENTIFIER;
REFERENCE;
PROPERTY_OR_FIELD;
INDEXER;
ARGLIST;
CONSTRUCTOR;
DATE_LITERAL;
HOLDER;
CONSTRUCTOR_ARRAY;
NAMED_ARGUMENT;
FUNCTIONREF;
TYPEREF;
RANGE;
VARIABLEREF;
LIST_INITIALIZER;
MAP_INITIALIZER;
LOCALVAR;
LOCALFUNC;
MAP_ENTRY;
METHOD;
ADD;
SUBTRACT;
// MULTIPLY;
// DIVIDE;
// MODULUS;
NUMBER;
}
// applies only to the parser:
@header {package org.springframework.expression.spel.generated;}
// applies only to the lexer:
@lexer::header {package org.springframework.expression.spel.generated;}
@rulecatch {
catch(RecognitionException e) {
//reportError(e);
throw e;
}
}
expr: expression EOF!;
exprList
: LPAREN expression (SEMI expression)+ (SEMIRPAREN | RPAREN)
-> ^(EXPRESSIONLIST expression+);
SEMIRPAREN : ';)'; // recoveryrelated: allows us to cope with a rogue superfluous semicolon before the rparen in an expression list
expression :
logicalOrExpression
( (ASSIGN^ logicalOrExpression)
| (DEFAULT^ logicalOrExpression)
| (QMARK^ expression COLON! expression))?;
parenExpr : LPAREN! expression RPAREN!;// (ROGUE! | RPAREN!);
logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*;
logicalAndExpression : relationalExpression (AND^ relationalExpression)*;
relationalExpression : sumExpression (relationalOperator^ sumExpression)?;
sumExpression
: productExpression ( (PLUS^ | MINUS^) productExpression)*;
// : left=productExpression (PLUS right+=productExpression)+ -> ^(ADD $left $right)
// | left=productExpression (MINUS right+=productExpression)+ -> ^(SUBTRACT $left $right)
// | productExpression;
// TODO could really do with changing ast node types here
productExpression
: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ;
// : left=powerExpr (STAR right+=powerExpr) -> ^(MULTIPLY $left $right)
// | left=powerExpr (DIV right+=powerExpr) -> ^(DIVIDE $left $right)
// | left=powerExpr (MOD right+=powerExpr) -> ^(MODULUS $left $right)
// | powerExpr;
powerExpr : unaryExpression (POWER^ unaryExpression)? ;
unaryExpression
: (PLUS^ | MINUS^ | BANG^) unaryExpression
| primaryExpression ;
primaryExpression
: startNode (node)? -> ^(EXPRESSION startNode (node)?);
startNode
:
(LPAREN expression SEMI) => exprList
| parenExpr
| methodOrProperty
| functionOrVar
| localFunctionOrVar
| reference
| indexer
| literal
| type
| constructor
| projection
| selection
| firstSelection
| lastSelection
| listInitializer
| mapInitializer
| lambda
// | attribute
;
node:
( methodOrProperty
| functionOrVar
| indexer
| projection
| selection
| firstSelection
| lastSelection
| exprList
| DOT
)+
;
functionOrVar
: (POUND ID LPAREN) => function
| var
;
function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs);
var : POUND id=ID -> ^(VARIABLEREF[$id]);
localFunctionOrVar
: (DOLLAR ID LPAREN) => localFunction
| localVar
;
localFunction : DOLLAR id=ID methodArgs -> ^(LOCALFUNC[$id] methodArgs);
localVar: DOLLAR id=ID -> ^(LOCALVAR[$id]);
methodOrProperty
: (ID LPAREN) => id=ID methodArgs -> ^(METHOD[$id] methodArgs)
| property
;
// may have to preserve these commas to make it easier to offer suggestions in the right place
// mod at 9th feb 19:13 - added the second 'COMMA?' to allow for code completion "foo(A,"
// TODO need to preserve commas and then check for badly formed call later (optimizing tree walk) to disallow "foo(a,b,c,)"
methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!;
// If we match ID then create a node called PROPERTY_OR_FIELD and copy the id info into it.
// this means the propertyOrField.text is what id.text would have been, rather than having to
// access id as a child of the new node.
property: id=ID -> ^(PROPERTY_OR_FIELD[$id]);
// start - in this block there are changes to help parser recovery and code completion
// fiddled with to support better code completion
// we preserve the colon and rparen to give positional info and the qualifiedId is optional to cope with
// code completing in @() (which is really an invalid expression)
reference
: AT pos=LPAREN (cn=contextName COLON)? (q=qualifiedId)? RPAREN
-> ^(REFERENCE[$pos] ($cn COLON)? $q? RPAREN);
// what I really want here is: was there a colon? position of the right paren
// end - in this block there are changes to help parser recovery and code completion
//indexer: LBRACKET r1=range (COMMA r2=range)* RBRACKET -> ^(INDEXER $r1 ($r2)*);
indexer: LBRACKET r1=argument (COMMA r2=argument)* RBRACKET -> ^(INDEXER $r1 ($r2)*);
//range: INTEGER_LITERAL UPTO^ INTEGER_LITERAL |
// argument;
// TODO make expression conditional with ? if want completion for when the RCURLY is missing
projection: PROJECT^ expression RCURLY!;
selection: SELECT^ expression RCURLY!;
firstSelection: SELECT_FIRST^ expression RCURLY!;
lastSelection: SELECT_LAST^ expression RCURLY!;
// TODO cope with array types
type: TYPE qualifiedId RPAREN -> ^(TYPEREF qualifiedId);
//type: TYPE tn=qualifiedId (LBRACKET RBRACKET)? (COMMA qid=qualifiedId)? RPAREN
//attribute
// : AT! LBRACKET! tn:qualifiedId! (ctorArgs)? RBRACKET!
// { #attribute = #([EXPR, tn_AST.getText(), "Spring.Expressions.AttributeNode"], #attribute); }
// ;
lambda
: LAMBDA (argList)? PIPE expression RCURLY -> ^(LAMBDA (argList)? expression);
argList : (id+=ID (COMMA id+=ID)*) -> ^(ARGLIST ($id)*);
constructor
: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs)
| arrayConstructor
;
arrayConstructor
: 'new' qualifiedId arrayRank (listInitializer)?
-> ^(CONSTRUCTOR_ARRAY qualifiedId arrayRank (listInitializer)?)
;
arrayRank
: LBRACKET (expression (COMMA expression)*)? RBRACKET -> ^(EXPRESSIONLIST expression*);
listInitializer
: LCURLY expression (COMMA expression)* RCURLY -> ^(LIST_INITIALIZER expression*);
//arrayInitializer
// : LCURLY expression (COMMA expression)* RCURLY -> ^(ARRAY_INITIALIZER expression*);
mapInitializer
: POUND LCURLY mapEntry (COMMA mapEntry)* RCURLY -> ^(MAP_INITIALIZER mapEntry*);
mapEntry
: expression COLON expression -> ^(MAP_ENTRY expression*);
ctorArgs
: LPAREN! (namedArgument (COMMA! namedArgument)*)? RPAREN!;
argument : expression;
namedArgument
: (ID ASSIGN) => id=ID ASSIGN expression
-> ^(NAMED_ARGUMENT[$id] expression)
| argument ;
qualifiedId : ID (DOT ID)* -> ^(QUALIFIED_IDENTIFIER ID*);
contextName : ID (DIV ID)* -> ^(QUALIFIED_IDENTIFIER ID*);
literal
: INTEGER_LITERAL
| STRING_LITERAL
| DQ_STRING_LITERAL
| boolLiteral
| NULL_LITERAL
| HEXADECIMAL_INTEGER_LITERAL
| REAL_LITERAL
| dateLiteral
;
boolLiteral: TRUE | FALSE;
dateLiteral: 'date' LPAREN d=STRING_LITERAL (COMMA f=STRING_LITERAL)? RPAREN -> ^(DATE_LITERAL $d ($f)?);
INTEGER_LITERAL
: (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
HEXADECIMAL_INTEGER_LITERAL : '0x' (HEX_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
relationalOperator
: EQUAL
| NOT_EQUAL
| LESS_THAN
| LESS_THAN_OR_EQUAL
| GREATER_THAN
| GREATER_THAN_OR_EQUAL
| IN
| IS
| BETWEEN
| LIKE
| MATCHES
| SOUNDSLIKE
| DISTANCETO
;
ASSIGN: '=';
EQUAL: '==';
NOT_EQUAL: '!=';
LESS_THAN: '<';
LESS_THAN_OR_EQUAL: '<=';
GREATER_THAN: '>';
GREATER_THAN_OR_EQUAL: '>=';
SOUNDSLIKE
: 'soundslike';
DISTANCETO
: 'distanceto';
IN: 'in';
IS: 'is';
BETWEEN:'between';
LIKE: 'like';
MATCHES:'matches';
NULL_LITERAL: 'null';
SEMI: ';';
DOT: '.';
COMMA: ',';
LPAREN: '(';
RPAREN: ')';
LCURLY: '{';
RCURLY: '}';
LBRACKET: '[';
RBRACKET: ']';
PIPE: '|';
AND: 'and';
OR: 'or';
FALSE: 'false';
TRUE: 'true';
PLUS: '+';
MINUS: '-';
DIV: '/';
STAR: '*';
MOD: '%';
POWER: '^';
BANG: '!';
POUND: '#';
QMARK: '?';
DEFAULT: '??';
LAMBDA: '{|';
PROJECT: '!{';
SELECT: '?{';
SELECT_FIRST: '^{';
SELECT_LAST: '${';
TYPE: 'T(';
STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
ID: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*;
DOT_ESCAPED: '\\.';
//DOUBLE_DOT: ':';
WS: ( ' ' | '\t' | '\n' |'\r')+ { $channel=HIDDEN; } ;
DOLLAR: '$';
AT: '@';
UPTO: '..';
COLON: ':';
/*
// real - use syntactic predicates (guess mode)
: ('.' DECIMAL_DIGIT) =>
in= '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?
| ((DECIMAL_DIGIT)+ '.' DECIMAL_DIGIT) =>
in=(DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?
| ((DECIMAL_DIGIT)+ (EXPONENT_PART)) =>
in= (DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?
| ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)) =>
in= (DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)
*/
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 APOS : '\''! '\'';
fragment DECIMAL_DIGIT : '0'..'9' ;
fragment INTEGER_TYPE_SUFFIX : ( 'UL' | 'LU' | 'ul' | 'lu' | 'uL' | 'lU' | 'U' | 'L' | 'u' | '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 : '+' | '-' ;
// TODO what is M or m?
fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd' | 'M' | 'm' ;

View File

@ -0,0 +1,92 @@
COMMA=51
GREATER_THAN_OR_EQUAL=79
EXPRESSIONLIST=4
GREATER_THAN=78
MINUS=41
NUMBER=29
ARGLIST=11
BANG=46
LESS_THAN=76
METHOD=26
FALSE=70
PROPERTY_OR_FIELD=9
INDEXER=10
CONSTRUCTOR_ARRAY=15
NULL_LITERAL=66
NAMED_ARGUMENT=16
PIPE=62
DOT=47
AND=39
EXPRESSION=6
LCURLY=63
DATE_LITERAL=13
REAL_TYPE_SUFFIX=92
QUALIFIED_IDENTIFIER=7
SELECT=57
STRING_LITERAL=64
SUBTRACT=28
RBRACKET=54
RPAREN=33
BETWEEN=82
SIGN=93
PLUS=40
INTEGER_LITERAL=5
AT=52
RANGE=19
SOUNDSLIKE=85
WS=89
DOLLAR=50
LESS_THAN_OR_EQUAL=77
HEXADECIMAL_INTEGER_LITERAL=67
LAMBDA=61
SEMI=31
EQUAL=74
DOT_ESCAPED=88
QMARK=36
COLON=37
PROJECT=55
DIV=43
REAL_LITERAL=68
ADD=27
TRUE=69
EXPONENT_PART=91
POUND=48
HOLDER=14
SELECT_FIRST=58
TYPE=60
MAP_ENTRY=25
SELECT_LAST=59
LBRACKET=53
MOD=44
FUNCTIONREF=17
OR=38
RCURLY=56
ASSIGN=34
LPAREN=30
HEX_DIGIT=73
LIST_INITIALIZER=21
APOS=87
ID=49
NOT_EQUAL=75
POWER=45
TYPEREF=18
DISTANCETO=86
DECIMAL_DIGIT=71
IS=81
SEMIRPAREN=32
DQ_STRING_LITERAL=65
LIKE=83
MAP_INITIALIZER=22
LOCALFUNC=24
IN=80
CONSTRUCTOR=12
INTEGER_TYPE_SUFFIX=72
MATCHES=84
UPTO=90
REFERENCE=8
DEFAULT=35
LOCALVAR=23
STAR=42
VARIABLEREF=20
'date'=95
'new'=94

View File

@ -0,0 +1,174 @@
lexer grammar SpringExpressions;
options {
language=Java;
}
@header {package org.springframework.expression.spel.generated;}
T94 : 'new' ;
T95 : 'date' ;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 62
SEMIRPAREN : ';)'; // recoveryrelated: allows us to cope with a rogue superfluous semicolon before the rparen in an expression list
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 263
INTEGER_LITERAL
: (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 266
HEXADECIMAL_INTEGER_LITERAL : '0x' (HEX_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 284
ASSIGN: '=';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 285
EQUAL: '==';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 286
NOT_EQUAL: '!=';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 287
LESS_THAN: '<';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 288
LESS_THAN_OR_EQUAL: '<=';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 289
GREATER_THAN: '>';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 290
GREATER_THAN_OR_EQUAL: '>=';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 291
SOUNDSLIKE
: 'soundslike';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 293
DISTANCETO
: 'distanceto';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 295
IN: 'in';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 296
IS: 'is';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 297
BETWEEN:'between';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 298
LIKE: 'like';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 299
MATCHES:'matches';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 300
NULL_LITERAL: 'null';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 302
SEMI: ';';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 303
DOT: '.';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 304
COMMA: ',';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 305
LPAREN: '(';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 306
RPAREN: ')';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 307
LCURLY: '{';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 308
RCURLY: '}';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 309
LBRACKET: '[';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 310
RBRACKET: ']';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 311
PIPE: '|';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 313
AND: 'and';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 314
OR: 'or';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 315
FALSE: 'false';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 316
TRUE: 'true';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 318
PLUS: '+';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 319
MINUS: '-';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 320
DIV: '/';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 321
STAR: '*';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 322
MOD: '%';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 323
POWER: '^';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 324
BANG: '!';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 325
POUND: '#';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 326
QMARK: '?';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 327
DEFAULT: '??';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 328
LAMBDA: '{|';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 329
PROJECT: '!{';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 330
SELECT: '?{';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 331
SELECT_FIRST: '^{';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 332
SELECT_LAST: '${';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 333
TYPE: 'T(';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 335
STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 336
DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 337
ID: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 338
DOT_ESCAPED: '\\.';
//DOUBLE_DOT: ':';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 340
WS: ( ' ' | '\t' | '\n' |'\r')+ { $channel=HIDDEN; } ;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 341
DOLLAR: '$';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 342
AT: '@';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 343
UPTO: '..';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 344
COLON: ':';
/*
// real - use syntactic predicates (guess mode)
: ('.' DECIMAL_DIGIT) =>
in= '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?
| ((DECIMAL_DIGIT)+ '.' DECIMAL_DIGIT) =>
in=(DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?
| ((DECIMAL_DIGIT)+ (EXPONENT_PART)) =>
in= (DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?
| ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)) =>
in= (DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)
*/
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 361
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 "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 367
fragment APOS : '\''! '\'';
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 368
fragment DECIMAL_DIGIT : '0'..'9' ;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 369
fragment INTEGER_TYPE_SUFFIX : ( 'UL' | 'LU' | 'ul' | 'lu' | 'uL' | 'lU' | 'U' | 'L' | 'u' | 'l' );
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 370
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 "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 372
fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* (DECIMAL_DIGIT)+ ;
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 373
fragment SIGN : '+' | '-' ;
// TODO what is M or m?
// $ANTLR src "/Users/aclement/el/spring3/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 375
fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd' | 'M' | 'm' ;

View File

@ -0,0 +1,39 @@
/*
* 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.internal;
import org.antlr.runtime.Token;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.ast.SpelNode;
public class EmptySpelNode extends SpelNode {
public EmptySpelNode(Token payload) {
super(payload);
}
@Override
public Object getValue(ExpressionState state) throws SpelException {
throw new RuntimeException("?");
}
@Override
public String toStringAST() {
return "<no string form node '" + getTokenName() + "'>";
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.internal;
import org.springframework.expression.spel.SpelException;
/**
* Wraps an ELException and can pass up through Antlr since it is unchecked, where it can then be unwrapped.
*
* @author Andy Clement
*/
public class InternalELException extends RuntimeException {
InternalELException(SpelException e) {
super(e);
}
@Override
public SpelException getCause() {
return (SpelException) super.getCause();
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.internal;
/**
* Special object that is used to wrap a map entry/value when iterating over a map. Providing a direct way for the
* expression to refer to either the key or value.
*
* @author Andy Clement
*/
public class KeyValuePair {
public Object key;
public Object value;
public KeyValuePair(Object k, Object v) {
this.key = k;
this.value = v;
}
}

View File

@ -0,0 +1,220 @@
/*
* 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.internal;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTreeAdaptor;
import org.springframework.expression.spel.ast.ArgList;
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.DateLiteral;
import org.springframework.expression.spel.ast.Dot;
import org.springframework.expression.spel.ast.ExpressionListNode;
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.IntLiteral;
import org.springframework.expression.spel.ast.Lambda;
import org.springframework.expression.spel.ast.ListInitializer;
import org.springframework.expression.spel.ast.LocalFunctionReference;
import org.springframework.expression.spel.ast.LocalVariableReference;
import org.springframework.expression.spel.ast.MapEntry;
import org.springframework.expression.spel.ast.MapInitializer;
import org.springframework.expression.spel.ast.MethodReference;
import org.springframework.expression.spel.ast.NullLiteral;
import org.springframework.expression.spel.ast.OperatorAnd;
import org.springframework.expression.spel.ast.OperatorBetween;
import org.springframework.expression.spel.ast.OperatorDistanceTo;
import org.springframework.expression.spel.ast.OperatorDivide;
import org.springframework.expression.spel.ast.OperatorEquality;
import org.springframework.expression.spel.ast.OperatorGreaterThan;
import org.springframework.expression.spel.ast.OperatorGreaterThanOrEqual;
import org.springframework.expression.spel.ast.OperatorIn;
import org.springframework.expression.spel.ast.OperatorInequality;
import org.springframework.expression.spel.ast.OperatorIs;
import org.springframework.expression.spel.ast.OperatorLessThan;
import org.springframework.expression.spel.ast.OperatorLessThanOrEqual;
import org.springframework.expression.spel.ast.OperatorLike;
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.OperatorSoundsLike;
import org.springframework.expression.spel.ast.Placeholder;
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.RealLiteral;
import org.springframework.expression.spel.ast.Reference;
import org.springframework.expression.spel.ast.Selection;
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;
import org.springframework.expression.spel.generated.SpringExpressionsLexer;
public class SpelTreeAdaptor extends CommonTreeAdaptor {
@Override
public Object create(Token payload) {
if (payload != null) {
switch (payload.getType()) {
case SpringExpressionsLexer.EXPRESSIONLIST:
return new ExpressionListNode(payload);
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 new IntLiteral(payload);
case SpringExpressionsLexer.HEXADECIMAL_INTEGER_LITERAL:
return new IntLiteral(payload, 16/* HEX */);
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.SOUNDSLIKE:
return new OperatorSoundsLike(payload);
case SpringExpressionsLexer.DISTANCETO:
return new OperatorDistanceTo(payload);
case SpringExpressionsLexer.PLUS:
return new OperatorPlus(payload);
case SpringExpressionsLexer.MINUS:
return new OperatorMinus(payload);
case SpringExpressionsLexer.STAR/* MULTIPLY */:
return new OperatorMultiply(payload);
case SpringExpressionsLexer.DIV/* DIVIDE */:
return new OperatorDivide(payload);
case SpringExpressionsLexer.MOD:
return new OperatorModulus(payload);
case SpringExpressionsLexer.STRING_LITERAL:
case SpringExpressionsLexer.DQ_STRING_LITERAL:
return new StringLiteral(payload);
case SpringExpressionsLexer.NULL_LITERAL:
return new NullLiteral(payload);
case SpringExpressionsLexer.DATE_LITERAL:
return new DateLiteral(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.REFERENCE:
return new Reference(payload);
case SpringExpressionsLexer.TYPEREF:
return new TypeReference(payload);
case SpringExpressionsLexer.EXPRESSION:
return new CompoundExpression(payload);
case SpringExpressionsLexer.LIST_INITIALIZER:
return new ListInitializer(payload);
case SpringExpressionsLexer.MAP_ENTRY:
return new MapEntry(payload);
case SpringExpressionsLexer.MAP_INITIALIZER:
return new MapInitializer(payload);
case SpringExpressionsLexer.CONSTRUCTOR:
return new ConstructorReference(payload, false);
case SpringExpressionsLexer.CONSTRUCTOR_ARRAY:
return new ConstructorReference(payload, true);
case SpringExpressionsLexer.LOCALFUNC:
return new LocalFunctionReference(payload);
case SpringExpressionsLexer.LOCALVAR:
return new LocalVariableReference(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.UPTO: return new Range(payload);
case SpringExpressionsLexer.IN:
return new OperatorIn(payload);
case SpringExpressionsLexer.LIKE:
return new OperatorLike(payload);
case SpringExpressionsLexer.BETWEEN:
return new OperatorBetween(payload);
case SpringExpressionsLexer.MATCHES:
return new OperatorMatches(payload);
case SpringExpressionsLexer.IS:
return new OperatorIs(payload);
case SpringExpressionsLexer.ARGLIST:
return new ArgList(payload);
case SpringExpressionsLexer.LAMBDA:
return new Lambda(payload);
case SpringExpressionsLexer.RPAREN:
return new Placeholder(payload);
case SpringExpressionsLexer.COLON:
return new Placeholder(payload);
// case SpringExpressionsLexer.EOF: return new ErrorNode(payload);
case SpringExpressionsLexer.DOT:
return new Dot(payload);
default:
throw new RuntimeException("Not implemented for '" + payload + "' " + getToken(payload) + "' "
+ payload.getType());
}
}
return new EmptySpelNode(payload);
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.internal;
import org.antlr.runtime.RecognitionException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.generated.SpringExpressionsLexer;
public class SpringExpressionsLexerExtender extends SpringExpressionsLexer {
public SpringExpressionsLexerExtender() {
super();
}
/**
* recover() attempts to provide better error messages once something has gone wrong. It then throws a
* InternalELException (has to be this unchecked exception as the exception must flow through Antlr lexer methods
* that do not have declared exceptions). The InternalELException will be caught at the top level and altered to
* include context (line,column) information before being rethrown.<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 InternalELException(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];
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.internal;
import org.antlr.runtime.BitSet;
import org.antlr.runtime.CommonTokenStream;
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.generated.SpringExpressionsParser;
public class SpringExpressionsParserExtender extends SpringExpressionsParser {
public SpringExpressionsParserExtender(TokenStream input) {
super(input);
}
/**
* This method does not actually recover but can attempt to produce better error messages than a generic Antlr
* parser because it knows about grammar specifics. would do
*/
@Override
public void recoverFromMismatchedToken(IntStream input, RecognitionException re, int ttype, BitSet follow)
throws RecognitionException {
CommonTokenStream tokStream = (CommonTokenStream) input;
int prevToken = tokStream.LA(-1);
int nextToken = tokStream.LA(1);
String prevTokenText = tokStream.LT(-1).getText();
String expectedToken = getTokenForId(ttype);
// Use token knowledge to log a more appropriate error:
// logger.error(ParserMessage.ERROR_NO_LEADING_ZERO, re.line, re.charPositionInLine);
throw re;
}
//
// /**
// * Similar to the BaseRecognizer getErrorMessage() but uses the ParserMessages class to get the text of the
// message
// */
// public void logError(RecognitionException re, String[] tokenNames) {
// logger.error(ELMessages.RECOGNITION_ERROR, re.line, re.charPositionInLine, re);
// }
@Override
public void recoverFromMismatchedSet(IntStream input, RecognitionException e, BitSet follow)
throws RecognitionException {
throw e;
}
@Override
public String getTokenErrorDisplay(Token t) {
if (t == null) {
return "<unknown>";
}
return super.getTokenErrorDisplay(t);
}
private String getTokenForId(int id) {
if (id == -1)
return "EOF";
return getTokenNames()[id];
}
}

View File

@ -0,0 +1,103 @@
/*
* 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.internal;
public enum TypeCode {
OBJECT(0, Object.class), BOOLEAN(1, Boolean.TYPE), BYTE(1, Byte.TYPE), CHAR(1, Character.TYPE), SHORT(2, Short.TYPE), INT(
3, Integer.TYPE), LONG(4, Long.TYPE), FLOAT(5, Float.TYPE), DOUBLE(6, Double.TYPE);
private int code;
private Class<?> type;
TypeCode(int code, Class<?> type) {
this.code = code;
this.type = type;
}
public Class<?> getType() {
return type;
}
public static TypeCode forClass(Class<?> c) {
TypeCode[] allValues = TypeCode.values();
for (int i = 0; i < allValues.length; i++) {
TypeCode typeCode = allValues[i];
if (c == typeCode.getType()) {
return typeCode;
}
}
return OBJECT;
}
/**
* For a primitive name this will determine the typecode value - supports
* int,byte,char,short,long,double,float,boolean
*/
public static TypeCode forName(String name) {
if (name.equals("int"))
return TypeCode.INT;
else if (name.equals("boolean"))
return TypeCode.BOOLEAN;
else if (name.equals("char"))
return TypeCode.CHAR;
else if (name.equals("long"))
return TypeCode.LONG;
else if (name.equals("float"))
return TypeCode.FLOAT;
else if (name.equals("double"))
return TypeCode.DOUBLE;
else if (name.equals("short"))
return TypeCode.SHORT;
else if (name.equals("byte"))
return TypeCode.BYTE;
return TypeCode.OBJECT;
}
public int getCode() {
return code;
}
public Object coerce(TypeCode fromTypeCode, Object fromObject) {
if (this == TypeCode.INT) {
switch (fromTypeCode) {
case BOOLEAN:
return ((Boolean) fromObject).booleanValue() ? 1 : 0;
}
}
//
// return Integer.valueOf
// } else if (this==TypeCode.BOOLEAN) {
// return new Boolean(left).intValue();
return null;
}
public static TypeCode forValue(Number op1) {
return forClass(op1.getClass());
}
public boolean isDouble() {
return this == DOUBLE;
}
public boolean isFloat() {
return this == FLOAT;
}
public boolean isLong() {
return this == LONG;
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.internal;
/**
* Utility methods (formatters, etc) used during parsing and evaluation.
*
* @author Andy Clement
*/
public class Utils {
/**
* Produce a nice string for a given method name with specified arguments.
*
* @param name the name of the method
* @param argumentTypes the types of the arguments to the method
* @return nicely formatted string, eg. foo(String,int)
*/
public static String formatMethodForMessage(String name, Class<?>... argumentTypes) {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append("(");
if (argumentTypes != null) {
for (int i = 0; i < argumentTypes.length; i++) {
if (i > 0)
sb.append(",");
sb.append(argumentTypes[i].getName());
}
}
sb.append(")");
return sb.toString();
}
/**
* Produce a nice string for a given class object. For example a string array will have the formatted name
* "java.lang.String[]".
*
* @param clazz The class whose name is to be formatted
* @return a formatted string suitable for message inclusion
*/
public static String formatClassnameForMessage(Class<?> clazz) {
if (clazz==null) {
return "null";
}
StringBuilder fmtd = new StringBuilder();
if (clazz.isArray()) {
int dims = 1;
Class baseClass = clazz.getComponentType();
while (baseClass.isArray()) {
baseClass = baseClass.getComponentType();
dims++;
}
fmtd.append(baseClass.getName());
for (int i = 0; i < dims; i++) {
fmtd.append("[]");
}
} else {
fmtd.append(clazz.getName());
}
return fmtd.toString();
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.internal;
import java.util.HashMap;
import java.util.Map;
/**
* A new scope is entered when a function is called and it is used to hold the parameters to the function call. If the names
* of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst
* the function is executing. When the function returns the scope is exited.
*
* @author Andy Clement
*
*/
public class VariableScope {
private final Map<String, Object> vars = new HashMap<String, Object>();
public VariableScope() { }
public VariableScope(Map<String, Object> arguments) {
if (arguments!=null) {
vars.putAll(arguments);
}
}
public VariableScope(String name,Object value) {
vars.put(name,value);
}
public Object lookupVariable(String name) {
return vars.get(name);
}
public void setVariable(String name, Object value) {
vars.put(name,value);
}
public boolean definesVariable(String name) {
return vars.containsKey(name);
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.internal;
import org.springframework.expression.spel.SpelException;
/**
* Wraps an ELException and can pass up through Antlr since it is unchecked, where it can then be unwrapped.
*
* @author Andy Clement
*/
public class WrappedExpressionException extends RuntimeException {
WrappedExpressionException(SpelException e) {
super(e);
}
@Override
public SpelException getCause() {
return (SpelException) super.getCause();
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.processors;
import java.util.Collection;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
// TODO 3 does it return an element consistent with input? (an int if input is ints, even though the average may be
// X.Y?) yes, for now
/**
* The AverageProcessor operates upon an input collection and computes the average value of the elements within it. It
* will currently only operate upon Numbers and its return value type is an Integer if the input values were integers,
* otherwise it is a double.
*/
public class AverageProcessor implements DataProcessor {
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws SpelException {
// TypeUtilities typeUtilities = state.getTypeUtilities();
boolean allIntegerObjects = true;
int total = 0;
int numberOfElements = 0;
for (Object element : input) {
if (element != null) {
if (element instanceof Number) {
allIntegerObjects = allIntegerObjects
&& (element.getClass() == Integer.class || element.getClass() == Integer.TYPE);
total = total + ((Number) element).intValue();
numberOfElements++;
} else {
throw new SpelException(SpelMessages.TYPE_NOT_SUPPORTED_BY_PROCESSOR, "average", element.getClass());
}
}
}
int result = total / numberOfElements;
// if (allIntegerObjects) {
// return new Integer(((Number) result).intValue());
// } else {
return result;
// }
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.processors;
import java.util.Collection;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* The count processor returns an Integer value representing the number of elements in the collection.
*
* @author Andy Clement
*
*/
public class CountProcessor implements DataProcessor {
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws SpelException {
return input.size();
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.processors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.ExpressionState;
public class CutProcessor implements DataProcessor {
/** Cut a piece out of a collection - the arguments are from (inclusive) to (exclusive) */
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws SpelException {
if (!(arguments[0] instanceof Integer && arguments[1] instanceof Integer)) {
throw new SpelException(SpelMessages.CUT_ARGUMENTS_MUST_BE_INTS, arguments[0].getClass().getName(),
arguments[1].getClass().getName());
}
int first = ((Integer) arguments[0]).intValue();
int last = ((Integer) arguments[1]).intValue();
List<Object> result = new ArrayList<Object>();
int pos = 0;
if (first < last) {
for (Object o : input) {
if (pos >= first && pos <= last)
result.add(o);
pos++;
}
} else {
for (Object o : input) {
if (pos >= last && pos <= first)
result.add(0, o);
pos++;
}
}
return result;
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.processors;
import java.util.Collection;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* A Data processor takes as input: a collection, an optional set of arguments that configure its behaviour, an
* evaluation context. It returns the result of processing the collection which might be as simple as adding up its
* elements or returning a subset of the non null elements.
*
* @author Andy Clement
*
*/
public interface DataProcessor {
Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws EvaluationException;
}

View File

@ -0,0 +1,43 @@
/*
* 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.processors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* The DistinctProcessor returns a new collection containing that is similar to the input collection but with all
* duplicate entries removed.
*
* @author Andy Clement
*
*/
public class DistinctProcessor implements DataProcessor {
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws SpelException {
List<Object> result = new ArrayList<Object>();
for (Object o : input) {
if (!result.contains(o))
result.add(o);
}
return result;
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.processors;
import java.util.Collection;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.ExpressionState;
/**
* The Max Processor returns the maximum element in the input collection, the maximum is determined using the comparator
* accessible in the evaluation context.
*
* @author Andy Clement
*
*/
public class MaxProcessor implements DataProcessor {
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws EvaluationException {
Object max = null;
TypeComparator comparator = state.getTypeComparator();
for (Object element : input) {
if (max == null) {
max = element;
} else {
if (comparator.compare(element, max) > 0)
max = element;
}
}
return max;
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.processors;
import java.util.Collection;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.ExpressionState;
/**
* The Min Processor returns the minimum element in the input collection, the minimum is determined using the comparator
* accessible in the evaluation context.
*
* @author Andy Clement
*
*/
public class MinProcessor implements DataProcessor {
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws EvaluationException {
Object minimum = null;
TypeComparator elementComparator = state.getTypeComparator();
for (Object element : input) {
if (minimum == null) {
minimum = element;
} else {
if (elementComparator.compare(element, minimum) < 0)
minimum = element;
}
}
return minimum;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.processors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* The NonNull Processor returns a new collection containing all non-null entries from the input collection.
*
* @author Andy Clement
*
*/
public class NonNullProcessor implements DataProcessor {
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws SpelException {
List<Object> l = new ArrayList<Object>();
for (Object o : input) {
if (o != null)
l.add(o);
}
return l;
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.processors;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.ExpressionState;
/**
* The Sort Processor will sort an input collection, comparing elements using the comparator accessible in the
* evaluation context.
*
* @author Andy Clement
*
*/
@SuppressWarnings("unchecked")
public class SortProcessor implements DataProcessor {
public Object process(Collection<?> input, Object[] arguments, ExpressionState state) throws EvaluationException {
List<Object> sortedCollection = new ArrayList<Object>();
sortedCollection.addAll(input);
LocalComparator comparator = new LocalComparator(state.getTypeComparator());
Collections.sort(sortedCollection, comparator);
if (comparator.exceptionOccurred != null)
throw comparator.exceptionOccurred;
return sortedCollection;
}
private static class LocalComparator implements java.util.Comparator {
TypeComparator comparator;
EvaluationException exceptionOccurred;
public LocalComparator(TypeComparator comparator) {
this.comparator = comparator;
}
public int compare(Object o1, Object o2) {
try {
return comparator.compare(o1, o2);
} catch (EvaluationException e) {
exceptionOccurred = e;
return 0;
}
}
}
}

View File

@ -0,0 +1,14 @@
<html>
<body>
<p>
Contains the registered collection processors available 'out of the box' with Spring
Expression Language. They are:<ul>
<li>sort() - sorts a collection
<li>nonnull() - returns a new collection containing the non-null elements from the input collection
<li>count() - returns the number of elements in the collection
<li>min() - returns the minimum element from the input collection
<li>max() - returns the maximum element from the input collection
<li>distinct() - returns a new collection where duplicates from the input collection have been removed
</p>
</body>
</html>

View File

@ -0,0 +1,68 @@
/*
* 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.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.EvaluationContext;
/**
* A simple CommandExecutor implementation that runs a constructor using reflective invocation.
*
* @author Andy Clement
*/
public class ReflectionConstructorExecutor implements ConstructorExecutor {
private final Constructor<?> c;
// When the constructor was found, we will have determined if arguments need to be converted for it
// to be invoked. Conversion won't be cheap so let's only do it if necessary.
private final Integer[] argsRequiringConversion;
public ReflectionConstructorExecutor(Constructor<?> constructor, Integer[] argsRequiringConversion) {
this.c = constructor;
this.argsRequiringConversion = argsRequiringConversion;
}
/**
* Invoke a constructor via reflection.
*/
public Object execute(EvaluationContext context, Object... arguments) throws AccessException {
if (argsRequiringConversion != null && arguments != null) {
ReflectionUtils.convertArguments(c.getParameterTypes(), c.isVarArgs(), context.getTypeUtils().getTypeConverter(), argsRequiringConversion, arguments);
}
if (c.isVarArgs()) {
arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(c.getParameterTypes(),arguments);
}
try {
if (!c.isAccessible()) {
c.setAccessible(true);
}
return c.newInstance(arguments);
} catch (IllegalArgumentException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
} catch (InstantiationException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.reflection;
import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredConstructor;
/**
* A constructor resolver that uses reflection to locate the constructor that should be invoked
*
* @author Andy Clement
*/
public class ReflectionConstructorResolver implements ConstructorResolver {
/*
* Indicates if this resolve will allow matches to be found that require some of the input arguments to be
* transformed by the conversion service.
*/
private boolean allowMatchesRequiringArgumentConversion = true;
public ReflectionConstructorResolver() {
}
public ReflectionConstructorResolver(boolean allowMatchesRequiringArgumentConversion) {
this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion;
}
public void setAllowMatchRequiringArgumentConversion(boolean allow) {
this.allowMatchesRequiringArgumentConversion = allow;
}
/**
* Locate a matching constructor or return null if non can be found.
*/
public ConstructorExecutor resolve(EvaluationContext context, String typename, Class<?>[] argumentTypes)
throws AccessException {
try {
Class<?> c = context.getTypeUtils().getTypeLocator().findType(typename);
DiscoveredConstructor dCtor = ReflectionUtils.findConstructor(context.getTypeUtils().getTypeConverter(), c,
argumentTypes, allowMatchesRequiringArgumentConversion);
if (dCtor == null) {
return null;
}
return new ReflectionConstructorExecutor(dCtor.theConstructor, dCtor.argumentsRequiringConversion);
} catch (EvaluationException e) {
throw new AccessException(null,e);
}
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodExecutor;
public class ReflectionMethodExecutor implements MethodExecutor {
private final Method m;
// When the method was found, we will have determined if arguments need to be converted for it
// to be invoked. Conversion won't be cheap so let's only do it if necessary.
private final Integer[] argsRequiringConversion;
public ReflectionMethodExecutor(Method theMethod, Integer[] argumentsRequiringConversion) {
this.m = theMethod;
this.argsRequiringConversion = argumentsRequiringConversion;
}
public Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
if (argsRequiringConversion != null && arguments != null) {
ReflectionUtils.convertArguments(m.getParameterTypes(),m.isVarArgs(),context.getTypeUtils().getTypeConverter(), argsRequiringConversion, arguments);
}
if (m.isVarArgs()) {
arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(),arguments);
}
try {
if (!m.isAccessible()) {
m.setAccessible(true);
}
return m.invoke(target, arguments);
} catch (IllegalArgumentException e) {
throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+ e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+ e.getMessage(), e);
} catch (InvocationTargetException e) {
e.getCause().printStackTrace();
throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+ e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.reflection;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredMethod;
/**
* A method resolver that uses reflection to locate the method that should be invoked
*
* @author Andy Clement
*/
public class ReflectionMethodResolver implements MethodResolver {
/*
* Indicates if this resolve will allow matches to be found that require some of the input arguments to be
* transformed by the conversion service.
*/
private boolean allowMatchesRequiringArgumentConversion = true;
public ReflectionMethodResolver() {
}
public ReflectionMethodResolver(boolean allowMatchesRequiringArgumentConversion) {
this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion;
}
public void setAllowMatchRequiringArgumentConversion(boolean allow) {
this.allowMatchesRequiringArgumentConversion = allow;
}
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class<?>[] argumentTypes) throws AccessException {
try {
Class<?> relevantClass = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
DiscoveredMethod dMethod = ReflectionUtils.findMethod(context.getTypeUtils().getTypeConverter(), name,
argumentTypes, relevantClass, allowMatchesRequiringArgumentConversion);
if (dMethod == null) {
return null;
}
return new ReflectionMethodExecutor(dMethod.theMethod, dMethod.argumentsRequiringConversion);
} catch (EvaluationException e) {
throw new AccessException(null,e);
}
}
}

View File

@ -0,0 +1,56 @@
package org.springframework.expression.spel.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyReaderExecutor;
public class ReflectionPropertyReaderExecutor implements PropertyReaderExecutor {
private Method methodToAccessProperty;
private Field fieldToAccessProperty;
private String propertyName;
public ReflectionPropertyReaderExecutor(String propertyName, Method method) {
this.propertyName = propertyName;
this.methodToAccessProperty = method;
}
public ReflectionPropertyReaderExecutor(String propertyName, Field field) {
this.propertyName = propertyName;
this.fieldToAccessProperty = field;
}
public Object execute(EvaluationContext context, Object target) throws AccessException {
if (fieldToAccessProperty != null) {
try {
if (!fieldToAccessProperty.isAccessible()) {
fieldToAccessProperty.setAccessible(true);
}
return fieldToAccessProperty.get(target);
} catch (IllegalArgumentException e) {
throw new AccessException("Unable to access field: " + propertyName, e);
} catch (IllegalAccessException e) {
throw new AccessException("Unable to access field: " + propertyName, e);
}
}
if (methodToAccessProperty != null) {
try {
if (!methodToAccessProperty.isAccessible())
methodToAccessProperty.setAccessible(true);
return methodToAccessProperty.invoke(target);
} catch (IllegalArgumentException e) {
throw new AccessException("Unable to access property '" + propertyName + "' through getter", e);
} catch (IllegalAccessException e) {
throw new AccessException("Unable to access property '" + propertyName + "' through getter", e);
} catch (InvocationTargetException e) {
throw new AccessException("Unable to access property '" + propertyName + "' through getter", e);
}
}
throw new AccessException("No method or field accessor found for property '" + propertyName + "'");
}
}

View File

@ -0,0 +1,21 @@
package org.springframework.expression.spel.reflection;
import java.lang.reflect.Array;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyReaderExecutor;
public class ReflectionPropertyReaderExecutorForArrayLength implements PropertyReaderExecutor {
public ReflectionPropertyReaderExecutorForArrayLength() {
}
public Object execute(EvaluationContext context, Object target) throws AccessException {
if (target.getClass().isArray()) {
return Array.getLength(target);
}
throw new AccessException("Cannot determine length of a non-array type '" + target.getClass() + "'");
}
}

View File

@ -0,0 +1,202 @@
/*
* 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.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.springframework.expression.CacheablePropertyAccessor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyReaderExecutor;
import org.springframework.expression.PropertyWriterExecutor;
/**
* Simple PropertyResolver that uses reflection to access properties for reading and writing. A property can be accessed
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written).
*
* @author Andy Clement
*/
public class ReflectionPropertyResolver extends CacheablePropertyAccessor {
public static boolean useResolverExecutorModel = true;
public boolean supportsResolverExecutorModel() {
return useResolverExecutorModel;
}
public PropertyReaderExecutor getReaderAccessor(EvaluationContext relatedContext, Object target, Object name) {
if (target==null) {
return null;
}
Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
if (!(name instanceof String)) {
return null; // TODO should raise an exception when the property-name is not a String?
}
String propertyName = (String) name;
if (relevantClass.isArray() && propertyName.equals("length")) {
return new ReflectionPropertyReaderExecutorForArrayLength();
}
Field field = ReflectionUtils.findField(propertyName, relevantClass);
if (field != null) {
return new ReflectionPropertyReaderExecutor(propertyName, field);
}
Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass);
if (m != null) {
return new ReflectionPropertyReaderExecutor(propertyName, m);
}
return null;
}
public PropertyWriterExecutor getWriterAccessor(EvaluationContext context, Object target, Object name) {
if (target==null) {
return null;
}
Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
if (!(name instanceof String)) {
return null;
}
Field field = ReflectionUtils.findField((String) name, relevantClass);
if (field != null) {
return new ReflectionPropertyWriterExecutor((String) name, field);
}
Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass);
if (m != null) {
return new ReflectionPropertyWriterExecutor((String) name, m);
}
return null;
}
/**
* Return true if the resolver is able to read the specified property from the specified target.
*/
// public boolean canRead(EvaluationContext relatedContext, Object target, Object name) throws AccessException {
// if (target==null) {
// return false;
// }
// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
// if (!(name instanceof String)) {
// return false; // TODO should raise an exception when the property-name is not a String?
// }
// String propertyName = (String) name;
// Field field = ReflectionUtils.findField(propertyName, relevantClass);
// if (field != null) {
// return true;
// }
// Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass);
// if (m != null) {
// return true;
// }
// return false;
// }
/**
* Read the specified property from the specified target.
// */
// public Object read(EvaluationContext context, Object target, Object name) throws AccessException {
// if (target==null) {
// return null;
// }
// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
// if (!(name instanceof String)) {
// return null; // TODO should raise an exception if the property cannot be found?
// }
// String propertyName = (String) name;
// Field field = ReflectionUtils.findField(propertyName, relevantClass);
// if (field != null) {
// try {
// if (!field.isAccessible()) {
// field.setAccessible(true);
// }
// return field.get(target);
// } catch (IllegalArgumentException e) {
// throw new AccessException("Unable to access field: " + name, e);
// } catch (IllegalAccessException e) {
// throw new AccessException("Unable to access field: " + name, e);
// }
// }
// Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass);
// if (m != null) {
// try {
// if (!m.isAccessible())
// m.setAccessible(true);
// return m.invoke(target);
// } catch (IllegalArgumentException e) {
// throw new AccessException("Unable to access property '" + name + "' through getter", e);
// } catch (IllegalAccessException e) {
// throw new AccessException("Unable to access property '" + name + "' through getter", e);
// } catch (InvocationTargetException e) {
// throw new AccessException("Unable to access property '" + name + "' through getter", e);
// }
// }
// return null;
// }
// public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException {
// if (target==null) {
// return;
// }
// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
// if (!(name instanceof String))
// return;
// Field field = ReflectionUtils.findField((String) name, relevantClass);
// if (field != null) {
// try {
// if (!field.isAccessible())
// field.setAccessible(true);
// field.set(target, newValue);
// } catch (IllegalArgumentException e) {
// throw new AccessException("Unable to write to property '" + name + "'", e);
// } catch (IllegalAccessException e) {
// throw new AccessException("Unable to write to property '" + name + "'", e);
// }
// }
// Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass);
// if (m != null) {
// try {
// if (!m.isAccessible())
// m.setAccessible(true);
// m.invoke(target, newValue);
// } catch (IllegalArgumentException e) {
// throw new AccessException("Unable to access property '" + name + "' through setter", e);
// } catch (IllegalAccessException e) {
// throw new AccessException("Unable to access property '" + name + "' through setter", e);
// } catch (InvocationTargetException e) {
// throw new AccessException("Unable to access property '" + name + "' through setter", e);
// }
// }
// }
public Class<?>[] getSpecificTargetClasses() {
return null; // this is a general purpose resolver that will try to access properties on any type!
}
// public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException {
// if (target==null) {
// return false;
// }
// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
// if (!(name instanceof String))
// return false;
// Field field = ReflectionUtils.findField((String) name, relevantClass);
// if (field != null)
// return true;
// Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass);
// if (m != null)
// return true;
// return false;
// }
}

View File

@ -0,0 +1,58 @@
package org.springframework.expression.spel.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.PropertyWriterExecutor;
public class ReflectionPropertyWriterExecutor implements PropertyWriterExecutor {
private Method methodToAccessProperty;
private Field fieldToAccessProperty;
private String propertyName;
public ReflectionPropertyWriterExecutor(String propertyName, Method method) {
this.propertyName = propertyName;
this.methodToAccessProperty = method;
}
public ReflectionPropertyWriterExecutor(String propertyName, Field field) {
this.propertyName = propertyName;
this.fieldToAccessProperty = field;
}
// public Object execute(EvaluationContext context, Object target) throws AccessException {
public void execute(EvaluationContext evaluationContext, Object target, Object newValue) throws AccessException {
if (fieldToAccessProperty != null) {
try {
if (!fieldToAccessProperty.isAccessible()) {
fieldToAccessProperty.setAccessible(true);
}
fieldToAccessProperty.set(target, newValue);
return;
} catch (IllegalArgumentException e) {
throw new AccessException("Unable to access field: " + propertyName, e);
} catch (IllegalAccessException e) {
throw new AccessException("Unable to access field: " + propertyName, e);
}
}
if (methodToAccessProperty != null) {
try {
if (!methodToAccessProperty.isAccessible())
methodToAccessProperty.setAccessible(true);
methodToAccessProperty.invoke(target, newValue);
return;
} catch (IllegalArgumentException e) {
throw new AccessException("Unable to access property '" + propertyName + "' through setter", e);
} catch (IllegalAccessException e) {
throw new AccessException("Unable to access property '" + propertyName + "' through setter", e);
} catch (InvocationTargetException e) {
throw new AccessException("Unable to access property '" + propertyName + "' through setter", e);
}
}
throw new AccessException("No method or field accessor found for property '" + propertyName + "'");
}
}

View File

@ -0,0 +1,612 @@
/*
* 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.reflection;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
/**
* Utility methods used by the reflection resolver code to discover the correct methods/constructors and fields that
* should be used in expressions.
*
* @author Andy Clement
*
*/
@SuppressWarnings("unchecked")
public class ReflectionUtils {
/**
* Locate a constructor on a type. There are three kinds of match that might occur:
* <ol>
* <li>An exact match where the types of the arguments match the types of the constructor
* <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor
* <li>A match where we are able to convert the arguments into those expected by the constructor, according to the
* registered type converter.
* </ol>
*
* @param typeConverter a converter that can be used to determine if the supplied arguments can be converted to
* expected arguments
* @param type the type being searched for a valid constructor
* @param argumentTypes the types of the arguments we want the constructor to have
* @return a DiscoveredConstructor object or null if non found
* @throws SpelException
*/
public static DiscoveredMethod findMethod(TypeConverter typeConverter, String name, Class<?>[] argumentTypes,
Class<?> type, boolean conversionAllowed) throws SpelException {
Method[] methods = type.getMethods();
Method closeMatch = null;
Integer[] argsToConvert = null;
boolean multipleOptions = false;
Method matchRequiringConversion = null;
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.isBridge()) {
continue;
}
if (method.getName().equals(name)) {
ArgumentsMatchInfo matchInfo = null;
if (method.isVarArgs() && argumentTypes.length>=(method.getParameterTypes().length-1)) {
// *sigh* complicated
matchInfo = compareArgumentsVarargs(method.getParameterTypes(), argumentTypes, typeConverter, conversionAllowed);
} else if (method.getParameterTypes().length == argumentTypes.length) {
// name and parameter number match, check the arguments
matchInfo = compareArguments(method.getParameterTypes(), argumentTypes, typeConverter, conversionAllowed);
}
if (matchInfo != null) {
if (matchInfo.kind == ArgsMatchKind.EXACT) {
return new DiscoveredMethod(method, null);
} else if (matchInfo.kind == ArgsMatchKind.CLOSE) {
if (matchRequiringConversion!=null) {
int stop = 1;
}
closeMatch = method;
} else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) {
if (matchRequiringConversion!=null) {
multipleOptions = true;
}
argsToConvert = matchInfo.argsRequiringConversion;
matchRequiringConversion = method;
}
}
}
}
if (closeMatch != null) {
return new DiscoveredMethod(closeMatch, null);
} else if (matchRequiringConversion != null) {
if (multipleOptions) {
throw new SpelException(SpelMessages.MULTIPLE_POSSIBLE_METHODS,name);
}
return new DiscoveredMethod(matchRequiringConversion, argsToConvert);
} else {
return null;
}
}
/**
* Locate a constructor on the type. There are three kinds of match that might occur:
* <ol>
* <li>An exact match where the types of the arguments match the types of the constructor
* <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor
* <li>A match where we are able to convert the arguments into those expected by the constructor, according to the
* registered type converter.
* </ol>
*
* @param typeConverter a converter that can be used to determine if the supplied arguments can be converted to
* expected arguments
* @param type the type being searched for a valid constructor
* @param argumentTypes the types of the arguments we want the constructor to have
* @return a DiscoveredConstructor object or null if non found
*/
public static DiscoveredConstructor findConstructor(TypeConverter typeConverter, Class<?> type,
Class<?>[] argumentTypes, boolean conversionAllowed) {
Constructor[] ctors = type.getConstructors();
Constructor closeMatch = null;
Integer[] argsToConvert = null;
Constructor matchRequiringConversion = null;
for (int i = 0; i < ctors.length; i++) {
Constructor ctor = ctors[i];
if (ctor.isVarArgs() && argumentTypes.length>=(ctor.getParameterTypes().length-1)) {
// *sigh* complicated
// Basically.. we have to have all parameters match up until the varargs one, then the rest of what is being provided should be
// the same type whilst the final argument to the method must be an array of that (oh, how easy...not) - or the final parameter
// we are supplied does match exactly (it is an array already).
ArgumentsMatchInfo matchInfo = compareArgumentsVarargs(ctor.getParameterTypes(), argumentTypes, typeConverter, conversionAllowed);
if (matchInfo != null) {
if (matchInfo.kind == ArgsMatchKind.EXACT) {
return new DiscoveredConstructor(ctor, null);
} else if (matchInfo.kind == ArgsMatchKind.CLOSE) {
closeMatch = ctor;
} else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) {
argsToConvert = matchInfo.argsRequiringConversion;
matchRequiringConversion = ctor;
}
}
} else if (ctor.getParameterTypes().length == argumentTypes.length) {
// worth a closer look
ArgumentsMatchInfo matchInfo = compareArguments(ctor.getParameterTypes(), argumentTypes, typeConverter,
conversionAllowed);
if (matchInfo != null) {
if (matchInfo.kind == ArgsMatchKind.EXACT) {
return new DiscoveredConstructor(ctor, null);
} else if (matchInfo.kind == ArgsMatchKind.CLOSE) {
closeMatch = ctor;
} else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) {
argsToConvert = matchInfo.argsRequiringConversion;
matchRequiringConversion = ctor;
}
}
}
}
if (closeMatch != null) {
return new DiscoveredConstructor(closeMatch, null);
} else if (matchRequiringConversion != null) {
return new DiscoveredConstructor(matchRequiringConversion, argsToConvert);
} else {
return null;
}
}
/**
* Compare argument arrays and return information about whether they match. A supplied type converter and
* conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
* type by the converter.
*
* @param expectedArgTypes the array of types the method/constructor is expecting
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
* @param typeConverter a registered type converter
* @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can
* match an expected type
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
*/
@SuppressWarnings("unchecked")
private static ArgumentsMatchInfo compareArguments(Class[] expectedArgTypes, Class[] suppliedArgTypes,
TypeConverter typeConverter, boolean conversionAllowed) {
ArgsMatchKind match = ArgsMatchKind.EXACT;
List<Integer> argsRequiringConversion = null;
for (int i = 0; i < expectedArgTypes.length && match != null; i++) {
Class suppliedArg = suppliedArgTypes[i];
Class expectedArg = expectedArgTypes[i];
if (expectedArg != suppliedArg) {
if (expectedArg.isAssignableFrom(suppliedArg) || areBoxingCompatible(expectedArg, suppliedArg)
/* || isWidenableTo(expectedArg, suppliedArg) */) {
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
match = ArgsMatchKind.CLOSE;
}
} else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
if (argsRequiringConversion == null) {
argsRequiringConversion = new ArrayList<Integer>();
}
argsRequiringConversion.add(i);
match = ArgsMatchKind.REQUIRES_CONVERSION;
} else {
match = null;
}
}
}
if (match == null) {
return null;
} else {
if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
return new ArgumentsMatchInfo(match, argsRequiringConversion.toArray(new Integer[] {}));
} else {
return new ArgumentsMatchInfo(match);
}
}
}
/**
* Compare argument arrays and return information about whether they match. A supplied type converter and
* conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
* type by the converter. This variant of compareArguments allows for a varargs match.
*
* @param expectedArgTypes the array of types the method/constructor is expecting
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
* @param typeConverter a registered type converter
* @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can
* match an expected type
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
*/
@SuppressWarnings("unchecked")
private static ArgumentsMatchInfo compareArgumentsVarargs(Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter, boolean conversionAllowed) {
ArgsMatchKind match = ArgsMatchKind.EXACT;
List<Integer> argsRequiringConversion = null;
// Check up until the varargs argument:
// Deal with the arguments up to 'expected number' - 1
for (int i = 0; i < expectedArgTypes.length - 1 && match != null; i++) {
Class suppliedArg = suppliedArgTypes[i];
Class expectedArg = expectedArgTypes[i];
if (expectedArg != suppliedArg) {
if (expectedArg.isAssignableFrom(suppliedArg) || areBoxingCompatible(expectedArg, suppliedArg)
/* || isWidenableTo(expectedArg, suppliedArg) */) {
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
match = ArgsMatchKind.CLOSE;
}
} else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
if (argsRequiringConversion == null) {
argsRequiringConversion = new ArrayList<Integer>();
}
argsRequiringConversion.add(i);
match = ArgsMatchKind.REQUIRES_CONVERSION;
} else {
match = null;
}
}
}
// Already does not match
if (match == null) {
return null;
}
// Special case: there is one parameter left and it is an array and it matches the varargs expected argument - that is a match, the caller has already built the array
if (suppliedArgTypes.length==expectedArgTypes.length && expectedArgTypes[expectedArgTypes.length-1]==suppliedArgTypes[suppliedArgTypes.length-1]) {
} else {
// Now... we have the final argument in the method we are checking as a match and we have 0 or more other arguments left to pass to it.
Class varargsParameterType = expectedArgTypes[expectedArgTypes.length-1].getComponentType();
// All remaining parameters must be of this type or convertable to this type
for (int i=expectedArgTypes.length-1;i<suppliedArgTypes.length;i++) {
Class suppliedArg = suppliedArgTypes[i];
if (varargsParameterType != suppliedArg) {
if (varargsParameterType.isAssignableFrom(suppliedArg) || areBoxingCompatible(varargsParameterType, suppliedArg)
/* || isWidenableTo(expectedArg, suppliedArg) */) {
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
match = ArgsMatchKind.CLOSE;
}
} else if (typeConverter.canConvert(suppliedArg, varargsParameterType)) {
if (argsRequiringConversion == null) {
argsRequiringConversion = new ArrayList<Integer>();
}
argsRequiringConversion.add(i);
match = ArgsMatchKind.REQUIRES_CONVERSION;
} else {
match = null;
}
}
}
}
if (match == null) {
return null;
} else {
if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
return new ArgumentsMatchInfo(match, argsRequiringConversion.toArray(new Integer[] {}));
} else {
return new ArgumentsMatchInfo(match);
}
}
}
private static boolean isDouble(Class c) {
return c == Double.class || c == Double.TYPE;
}
private static boolean isFloat(Class c) {
return c == Float.class || c == Float.TYPE;
}
private static boolean isLong(Class c) {
return c == Long.class || c == Long.TYPE;
}
private static boolean isInt(Class c) {
return c == Integer.class || c == Integer.TYPE;
}
private static boolean isChar(Class c) {
return c == Character.class || c == Character.TYPE;
}
private static boolean isShort(Class c) {
return c == Short.class || c == Short.TYPE;
}
private static boolean isByte(Class c) {
return c == Byte.class || c == Byte.TYPE;
}
/**
* Returns true if the input-type can be 'widened' to the target-type, according to the following allowed widenings:
*
* byte to short, int, long, float, or double <BR>
* short to int, long, float, or double <BR>
* char to int, long, float, or double <BR>
* int to long, float, or double <BR>
* long to float or double <BR>
* float to double
*/
private static boolean isWidenableTo(Class targetType, Class inputType) {
if (inputType.isPrimitive()) {
if (inputType == Double.TYPE) {
return (isDouble(targetType));
} else if (inputType == Long.TYPE) {
return (isDouble(targetType) || isFloat(targetType));
} else if (inputType == Integer.TYPE) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType);
} else if (inputType == Character.TYPE) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType) || isInt(targetType);
} else if (inputType == Short.TYPE) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType) || isInt(targetType);
} else if (inputType == Byte.TYPE) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType) || isInt(targetType)
|| isShort(targetType);
}
} else {
if (inputType == Double.class) {
return (isDouble(targetType));
} else if (inputType == Long.class) {
return (isDouble(targetType) || isFloat(targetType));
} else if (inputType == Integer.class) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType);
} else if (inputType == Character.class) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType) || isInt(targetType);
} else if (inputType == Short.class) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType) || isInt(targetType);
} else if (inputType == Byte.class) {
return isDouble(targetType) || isFloat(targetType) || isLong(targetType) || isInt(targetType)
|| isShort(targetType);
}
}
return false;
}
// TODO optimize impl
private static boolean areBoxingCompatible(Class class1, Class class2) {
if (class1 == Integer.class && class2 == Integer.TYPE)
return true;
if (class1 == Float.class && class2 == Float.TYPE)
return true;
if (class1 == Double.class && class2 == Double.TYPE)
return true;
if (class1 == Short.class && class2 == Short.TYPE)
return true;
if (class1 == Long.class && class2 == Long.TYPE)
return true;
if (class1 == Boolean.class && class2 == Boolean.TYPE)
return true;
if (class1 == Character.class && class2 == Character.TYPE)
return true;
if (class1 == Byte.class && class2 == Byte.TYPE)
return true;
if (class2 == Integer.class && class1 == Integer.TYPE)
return true;
if (class2 == Float.class && class1 == Float.TYPE)
return true;
if (class2 == Double.class && class1 == Double.TYPE)
return true;
if (class2 == Short.class && class1 == Short.TYPE)
return true;
if (class2 == Long.class && class1 == Long.TYPE)
return true;
if (class2 == Boolean.class && class1 == Boolean.TYPE)
return true;
if (class2 == Character.class && class1 == Character.TYPE)
return true;
if (class2 == Byte.class && class1 == Byte.TYPE)
return true;
return false;
}
/**
* Find a field of a certain name on a specified class
*/
public final static Field findField(String name, Class<?> clazz) {
Field[] fields = clazz.getFields(); // TODO what about inherited fields? try getFields() too?
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
/**
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
* 'get' and the rest of the name is the same as the property name (with the first character uppercased).
*/
public static Method findGetterForProperty(String propertyName, Class<?> clazz) {
Method[] ms = clazz.getMethods();
StringBuilder sb = new StringBuilder();
sb.append("get").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1));
String expectedGetterName = sb.toString();
for (int i = 0; i < ms.length; i++) {
Method method = ms[i];
if (method.getParameterTypes().length == 0 && method.getName().equals(expectedGetterName)) {
return method;
}
}
return null;
}
/**
* Find a setter method for the specified property
*/
public static Method findSetterForProperty(String propertyName, Class<?> clazz) {
Method[] ms = clazz.getMethods();
StringBuilder sb = new StringBuilder();
sb.append("set").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1));
String setterName = sb.toString();
for (int i = 0; i < ms.length; i++) {
Method method = ms[i];
if (method.getParameterTypes().length == 1 && method.getName().equals(setterName)) {
return method;
}
}
return null;
}
/**
* An instance of MatchInfo describes what kind of match was achieved between two sets of arguments - the set that a
* method/constructor is expecting and the set that are being supplied at the point of invocation. If the kind
* indicates that conversion is required for some of the arguments then the arguments that require conversion are
* listed in the argsRequiringConversion array.
*
*/
private static class ArgumentsMatchInfo {
ArgsMatchKind kind;
Integer[] argsRequiringConversion;
ArgumentsMatchInfo(ArgsMatchKind kind, Integer[] integers) {
this.kind = kind;
this.argsRequiringConversion = integers;
}
ArgumentsMatchInfo(ArgsMatchKind kind) {
this.kind = kind;
}
}
private static enum ArgsMatchKind {
EXACT, CLOSE, REQUIRES_CONVERSION;
}
/**
* When a match is found searching for a particular constructor, this object captures the constructor object and
* details of which arguments require conversion for the call to be allowed.
*/
public static class DiscoveredConstructor {
public Constructor theConstructor;
public Integer[] argumentsRequiringConversion;
public DiscoveredConstructor(Constructor theConstructor, Integer[] argsToConvert) {
this.theConstructor = theConstructor;
this.argumentsRequiringConversion = argsToConvert;
}
}
/**
* When a match is found searching for a particular method, this object captures the method object and details of
* which arguments require conversion for the call to be allowed.
*/
public static class DiscoveredMethod {
public Method theMethod;
public Integer[] argumentsRequiringConversion;
public DiscoveredMethod(Method theMethod, Integer[] argsToConvert) {
this.theMethod = theMethod;
this.argumentsRequiringConversion = argsToConvert;
}
}
static void convertArguments(Class[] parameterTypes,boolean isVarargs, TypeConverter converter ,Integer[] argsRequiringConversion, Object... arguments) throws AccessException {
Class varargsType = null;
if (isVarargs) {
varargsType = parameterTypes[parameterTypes.length-1].getComponentType();
}
for (int i = 0; i < argsRequiringConversion.length; i++) {
int argPosition = argsRequiringConversion[i];
Class targetType = null;
if (isVarargs && argPosition>=(parameterTypes.length-1)) {
targetType = varargsType;
} else {
targetType = parameterTypes[argPosition];
}
try {
arguments[argPosition] = converter.convertValue(arguments[argPosition],targetType);
} catch (EvaluationException e) {
throw new AccessException("Converter failed to convert '" + arguments[argPosition] + " to type '"
+ targetType + "'", e);
}
}
}
static void convertArguments(Class[] parameterTypes,boolean isVarargs, TypeConverter converter , Object... arguments) throws AccessException {
Class varargsType = null;
if (isVarargs) {
varargsType = parameterTypes[parameterTypes.length-1].getComponentType();
}
for (int i = 0; i < arguments.length; i++) {
Class targetType = null;
if (isVarargs && i>=(parameterTypes.length-1)) {
targetType = varargsType;
} else {
targetType = parameterTypes[i];
}
try {
if (arguments[i]!=null && arguments[i].getClass()!=targetType) {
arguments[i] = converter.convertValue(arguments[i],targetType);
}
} catch (EvaluationException e) {
throw new AccessException("Converter failed to convert '" + arguments[i] + " to type '"
+ targetType + "'", e);
}
}
}
/**
* Package up the arguments so that they correctly match what is expected in parameterTypes. For example, if parameterTypes is (int, String[])
* because the second parameter was declared String... then if arguments is [1,"a","b"] then it must be repackaged as [1,new String[]{"a","b"}]
* in order to match the expected parameterTypes.
*
* @param parameterTypes the types of the parameters for the invocation
* @param arguments the arguments to be setup ready for the invocation
* @return a repackaged array of arguments where any varargs setup has been done
*/
static Object[] setupArgumentsForVarargsInvocation(Class[] parameterTypes, Object... arguments) {
// Check if array already built for final argument
int nParams = parameterTypes.length;
int nArgs = arguments.length;
// Check if repackaging is needed:
if (nParams!=arguments.length || parameterTypes[nParams-1]!=(arguments[nArgs-1]==null?null:arguments[nArgs-1].getClass())) {
int arraySize = 0; // zero size array if nothing to pass as the varargs parameter
if (arguments!=null && nArgs>=nParams) {
arraySize = nArgs-(nParams-1);
}
Object[] repackagedArguments = (Object[])Array.newInstance(parameterTypes[nParams-1].getComponentType(),arraySize);
// Copy all but the varargs arguments
for (int i=0;i<arraySize;i++) {
repackagedArguments[i] = arguments[nParams+i-1];
}
// Create an array for the varargs arguments
Object[] newArgs = new Object[nParams];
for (int i=0;i<newArgs.length - 1;i++) {
newArgs[i] = arguments[i];
}
newArgs[newArgs.length-1] = repackagedArguments;
return newArgs;
}
return arguments;
}
public static Object[] prepareArguments(TypeConverter converter, Method m, Object[] arguments) throws AccessException {//TODO should not be accessexception
if (arguments != null) {
ReflectionUtils.convertArguments(m.getParameterTypes(),m.isVarArgs(),converter, arguments);
}
if (m.isVarArgs()) {
arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(),arguments);
}
return arguments;
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.standard;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
/**
* A simple basic TypeComparator implementation. It supports comparison of numbers and types implementing Comparable.
*
* @author Andy Clement
*/
public class StandardComparator implements TypeComparator {
public boolean canCompare(Object left, Object right) {
if (left == null || right == null) {
return true;
}
if (left instanceof Number && right instanceof Number) {
return true;
}
if (left.getClass() == right.getClass() && left instanceof Comparable) {
return true;
}
return false;
}
@SuppressWarnings("unchecked")
public int compare(Object left, Object right) throws SpelException {
// If one is null, check if the other is
if (left == null) {
return right == null ? 0 : 1;
} else if (right == null) {
return left == null ? 0 : -1;
}
// Basic number comparisons
if (left instanceof Number && right instanceof Number) {
Number leftNumber = (Number) left;
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
Double d1 = leftNumber.doubleValue();
Double d2 = rightNumber.doubleValue();
return d1.compareTo(d2);
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
Float f1 = leftNumber.floatValue();
Float f2 = rightNumber.floatValue();
return f1.compareTo(f2);
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
Long l1 = leftNumber.longValue();
Long l2 = rightNumber.longValue();
return l1.compareTo(l2);
} else {
Integer i1 = leftNumber.intValue();
Integer i2 = rightNumber.intValue();
return i1.compareTo(i2);
}
}
if (left.getClass() == right.getClass() && left instanceof Comparable) {
return ((Comparable) left).compareTo(right);
} else {
throw new SpelException(SpelMessages.NOT_COMPARABLE, left.getClass(), right.getClass());
}
}
}

View File

@ -0,0 +1,193 @@
/*
* 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.standard;
import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.TypeUtils;
import org.springframework.expression.spel.reflection.ReflectionConstructorResolver;
import org.springframework.expression.spel.reflection.ReflectionMethodResolver;
import org.springframework.expression.spel.reflection.ReflectionPropertyResolver;
/**
* Provides a default EvaluationContext implementation.
* <p>
* To resolved properties/methods/fields this context uses a reflection mechanism.
*
* @author Andy Clement
*
*/
public class StandardEvaluationContext implements EvaluationContext {
private Object rootObject;
private StandardTypeUtilities typeUtils;
private final Map<String, Object> variables = new HashMap<String, Object>();
private final List<MethodResolver> methodResolvers = new ArrayList<MethodResolver>();
private final List<ConstructorResolver> constructorResolvers = new ArrayList<ConstructorResolver>();
private final List<PropertyAccessor> propertyResolvers = new ArrayList<PropertyAccessor>();
private final Map<String, Map<String, Object>> simpleReferencesMap = new HashMap<String, Map<String, Object>>();
public StandardEvaluationContext() {
typeUtils = new StandardTypeUtilities();
addMethodResolver(new ReflectionMethodResolver());
addConstructorResolver(new ReflectionConstructorResolver());
addPropertyAccessor(new ReflectionPropertyResolver());
}
public StandardEvaluationContext(Object rootContextObject) {
this();
this.rootObject = rootContextObject;
}
public void setClassLoader(ClassLoader loader) {
TypeLocator tLocator = typeUtils.getTypeLocator();
if (tLocator instanceof StandardTypeLocator) {
((StandardTypeLocator) tLocator).setClassLoader(loader);
}
}
public void registerImport(String importPrefix) {
TypeLocator tLocator = typeUtils.getTypeLocator();
if (tLocator instanceof StandardTypeLocator) {
((StandardTypeLocator) tLocator).registerImport(importPrefix);
}
}
public void setClasspath(String classpath) {
StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator);
List<URL> urls = new ArrayList<URL>();
while (st.hasMoreTokens()) {
String element = st.nextToken();
try {
urls.add(new File(element).toURL());
} catch (MalformedURLException e) {
throw new RuntimeException("Invalid element in classpath " + element);
}
}
ClassLoader cl = new URLClassLoader(urls.toArray(new URL[] {}), Thread.currentThread().getContextClassLoader());
TypeLocator tLocator = typeUtils.getTypeLocator();
if (tLocator instanceof StandardTypeLocator) {
((StandardTypeLocator) tLocator).setClassLoader(cl);
}
}
public Object lookupVariable(String name) {
return variables.get(name);
}
public TypeUtils getTypeUtils() {
return typeUtils;
}
public Object getRootContextObject() {
return rootObject;
}
public Object lookupReference(Object contextName, Object objectName) {
String contextToLookup = (contextName == null ? "root" : (String) contextName);
// if (contextName==null) return simpleReferencesMap;
Map<String, Object> contextMap = simpleReferencesMap.get(contextToLookup);
if (contextMap == null)
return null;
if (objectName == null)
return contextMap;
return contextMap.get(objectName);
}
public List<PropertyAccessor> getPropertyAccessors() {
return propertyResolvers;
}
public void addPropertyAccessor(PropertyAccessor accessor) {
propertyResolvers.add(accessor);
}
public void removePropertyAccessor(PropertyAccessor accessor) {
propertyResolvers.remove(accessor);
}
public void insertPropertyAccessor(int position,PropertyAccessor accessor) {
propertyResolvers.add(position,accessor);
}
public List<MethodResolver> getMethodResolvers() {
return methodResolvers;
}
public List<ConstructorResolver> getConstructorResolvers() {
return constructorResolvers;
}
public void setVariable(String name, Object value) {
variables.put(name, value);
}
public void registerFunction(String name, Method m) {
variables.put(name, m);
}
public void setRootObject(Object o) {
this.rootObject = o;
}
// TODO 3 have a variant that adds at position (same for ctor/propOrField)
public void addMethodResolver(MethodResolver resolver) {
methodResolvers.add(resolver);
}
public void removeMethodResolver(MethodResolver resolver) {
methodResolvers.remove(resolver);
}
public void insertMethodResolver(int pos, MethodResolver resolver) {
methodResolvers.add(pos, resolver);
}
public void addConstructorResolver(ConstructorResolver resolver) {
constructorResolvers.add(resolver);
}
public void addReference(String contextName, String objectName, Object value) {
Map<String, Object> contextMap = simpleReferencesMap.get(contextName);
if (contextMap == null) {
contextMap = new HashMap<String, Object>();
simpleReferencesMap.put(contextName, contextMap);
}
contextMap.put(objectName, value);
}
public void addTypeConverter(StandardIndividualTypeConverter newConverter) {
((StandardTypeConverter)typeUtils.getTypeConverter()).registerConverter(newConverter);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2004-2007 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 org.springframework.expression.EvaluationException;
/**
* Implementations of this interface are able to convert from some set of types to another type. For
* example they might be able to convert some set of number types (Integer.class, Double.class) to
* a string (String.class). Once created they are registered with the {@link StandardEvaluationContext} or
* {@link StandardTypeConverter}.
*
* @author Andy Clement
*/
public interface StandardIndividualTypeConverter {
/**
* @return return the set of classes which this converter can convert from.
*/
Class<?>[] getFrom();
/**
* @return the class which this converter can convert to.
*/
Class<?> getTo();
/**
* Return a value converted to the type that {@link #getTo()} specified.
*
* @param value the object to convert
* @return the converted value
* @throws EvaluationException if there is a problem during conversion
*/
Object convert(Object value) throws EvaluationException;
}

View File

@ -0,0 +1,283 @@
/*
* 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.standard;
import java.util.HashMap;
import java.util.Map;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
public class StandardTypeConverter implements TypeConverter {
public Map<Class<?>, Map<Class<?>, StandardIndividualTypeConverter>> converters = new HashMap<Class<?>, Map<Class<?>, StandardIndividualTypeConverter>>();
StandardTypeConverter() {
registerConverter(new ToBooleanConverter());
registerConverter(new ToCharacterConverter());
registerConverter(new ToShortConverter());
registerConverter(new ToLongConverter());
registerConverter(new ToDoubleConverter());
registerConverter(new ToFloatConverter());
registerConverter(new ToStringConverter());
registerConverter(new ToIntegerConverter());
registerConverter(new ToByteConverter());
}
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
Map<Class<?>, StandardIndividualTypeConverter> possibleConvertersToTheTargetType = converters.get(targetType);
if (possibleConvertersToTheTargetType == null && targetType.isPrimitive()) {
if (targetType == Integer.TYPE)
possibleConvertersToTheTargetType = converters.get(Integer.class);
else if (targetType == Boolean.TYPE)
possibleConvertersToTheTargetType = converters.get(Boolean.class);
else if (targetType == Short.TYPE)
possibleConvertersToTheTargetType = converters.get(Short.class);
else if (targetType == Long.TYPE)
possibleConvertersToTheTargetType = converters.get(Long.class);
else if (targetType == Character.TYPE)
possibleConvertersToTheTargetType = converters.get(Character.class);
else if (targetType == Double.TYPE)
possibleConvertersToTheTargetType = converters.get(Double.class);
else if (targetType == Float.TYPE)
possibleConvertersToTheTargetType = converters.get(Float.class);
else if (targetType == Byte.TYPE)
possibleConvertersToTheTargetType = converters.get(Byte.class);
}
if (possibleConvertersToTheTargetType != null) {
StandardIndividualTypeConverter aConverter = possibleConvertersToTheTargetType.get(sourceType);
if (aConverter != null) {
return true;
}
}
return false;
}
// TODO 3 Q In case of a loss in information with coercion to a narrower type, should we throw an exception?
public Object convertValue(Object value, Class<?> targetType) throws SpelException {
if (value==null || value.getClass()==targetType) return value;
Map<Class<?>, StandardIndividualTypeConverter> possibleConvertersToTheTargetType = converters.get(targetType);
if (possibleConvertersToTheTargetType == null && targetType.isPrimitive()) {
if (targetType == Integer.TYPE)
possibleConvertersToTheTargetType = converters.get(Integer.class);
else if (targetType == Boolean.TYPE)
possibleConvertersToTheTargetType = converters.get(Boolean.class);
else if (targetType == Short.TYPE)
possibleConvertersToTheTargetType = converters.get(Short.class);
else if (targetType == Long.TYPE)
possibleConvertersToTheTargetType = converters.get(Long.class);
else if (targetType == Character.TYPE)
possibleConvertersToTheTargetType = converters.get(Character.class);
else if (targetType == Double.TYPE)
possibleConvertersToTheTargetType = converters.get(Double.class);
else if (targetType == Float.TYPE)
possibleConvertersToTheTargetType = converters.get(Float.class);
else if (targetType == Byte.TYPE)
possibleConvertersToTheTargetType = converters.get(Byte.class);
}
Object result = null;
if (possibleConvertersToTheTargetType != null) {
StandardIndividualTypeConverter aConverter = possibleConvertersToTheTargetType.get(value.getClass());
if (aConverter != null) {
try {
result = aConverter.convert(value);
} catch (EvaluationException ee) {
if (ee instanceof SpelException) {
throw (SpelException)ee;
} else {
throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION,ee.getMessage());
}
}
}
}
if (result != null)
return result;
throw new SpelException(SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), targetType);
}
public void registerConverter(StandardIndividualTypeConverter aConverter) {
Class<?> toType = aConverter.getTo();
Map<Class<?>, StandardIndividualTypeConverter> convertersResultingInSameType = converters.get(toType);
if (convertersResultingInSameType == null) {
convertersResultingInSameType = new HashMap<Class<?>, StandardIndividualTypeConverter>();
}
Class<?>[] fromTypes = aConverter.getFrom();
for (int i = 0; i < fromTypes.length; i++) {
convertersResultingInSameType.put(fromTypes[i], aConverter);
}
converters.put(aConverter.getTo(), convertersResultingInSameType);
}
private static class ToBooleanConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
return ((Boolean) value).booleanValue();
}
public Class<?>[] getFrom() {
return new Class<?>[] { Boolean.class };
}
public Class<?> getTo() {
return Boolean.TYPE;
}
}
private static class ToDoubleConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
if (value instanceof Double) {
return ((Double) value).doubleValue();
} else if (value instanceof String) {
try {
Double.parseDouble((String) value);
} catch (NumberFormatException nfe) {
// TODO 3 Q throw something or leave the caller to throw a conversion exception?
}
} else if (value instanceof Integer) {
return new Double(((Integer) value).intValue());
}
return null;
}
public Class<?>[] getFrom() {
return new Class<?>[] { Double.class, String.class, Integer.class };
}
public Class<?> getTo() {
return Double.class;
}
}
private static class ToFloatConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
return ((Double) value).floatValue();
}
public Class<?>[] getFrom() {
return new Class<?>[] { Double.class };
}
public Class<?> getTo() {
return Float.class;
}
}
private static class ToByteConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
return ((Integer) value).byteValue();
}
public Class<?>[] getFrom() {
return new Class<?>[] { Integer.class };
}
public Class<?> getTo() {
return Byte.class;
}
}
private static class ToLongConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
if (value instanceof Integer)
return ((Integer) value).longValue();
else if (value instanceof Short)
return ((Short) value).longValue();
else if (value instanceof Byte)
return ((Byte) value).longValue();
return null;
}
public Class<?>[] getFrom() {
return new Class<?>[] { Integer.class, Short.class, Byte.class };
}
public Class<?> getTo() {
return Long.class;
}
}
private static class ToCharacterConverter implements StandardIndividualTypeConverter {
public Character convert(Object value) throws SpelException {
if (value instanceof Integer)
return ((char) ((Integer) value).intValue());
if (value instanceof String) {
String s = (String) value;
if (s.length() == 1)
return s.charAt(0);
}
return null;
}
public Class<?>[] getFrom() {
return new Class<?>[] { Integer.class, String.class };
}
public Class<?> getTo() {
return Character.class;
}
}
private static class ToShortConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
if (value instanceof Integer)
return ((short) ((Integer) value).shortValue());
return null;
}
public Class<?>[] getFrom() {
return new Class<?>[] { Integer.class };
}
public Class<?> getTo() {
return Short.class;
}
}
private static class ToStringConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
return value.toString();
}
public Class<?>[] getFrom() {
return new Class<?>[] { Integer.class, Double.class };
}
public Class<?> getTo() {
return String.class;
}
}
private static class ToIntegerConverter implements StandardIndividualTypeConverter {
public Object convert(Object value) throws SpelException {
if (value instanceof Integer)
return ((Integer) value).intValue();
else if (value instanceof Long)
return ((Long) value).intValue();
else
return null;
}
public Class<?>[] getFrom() {
return new Class<?>[] { Integer.class, Long.class };
}
public Class<?> getTo() {
return Integer.TYPE;
}
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.standard;
import java.util.ArrayList;
import java.util.List;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
// TODO 3 promote import prefix support and classloader setting to the interface?
public class StandardTypeLocator implements TypeLocator {
private ClassLoader loader;
private final List<String> knownPackagePrefixes = new ArrayList<String>();
public StandardTypeLocator() {
loader = Thread.currentThread().getContextClassLoader();
registerImport("java.lang");
registerImport("java.util");
registerImport("java.awt");
}
// OPTIMIZE I'm sure this *could* be more inefficient if I tried really hard...
public Class<?> findType(String type) throws EvaluationException {
String nameToLookup = type;
try {
Class<?> c = loader.loadClass(nameToLookup);
return c;
} catch (ClassNotFoundException e) {
// might need a prefix...
}
// try prefixes
for (String prefix : knownPackagePrefixes) {
try {
nameToLookup = new StringBuilder().append(prefix).append(".").append(type).toString();
Class<?> c = loader.loadClass(nameToLookup);
return c;
} catch (ClassNotFoundException e) {
}
}
// TODO should some of these common messages be promoted to top level exception types?
throw new SpelException(SpelMessages.TYPE_NOT_FOUND, type);
}
/**
* Register a new import prefix that will be used when searching for unqualified types. Expected format is something
* like "java.lang"
*/
public void registerImport(String prefix) {
knownPackagePrefixes.add(prefix);
}
public void unregisterImport(String prefix) {
knownPackagePrefixes.add(prefix);
}
public List<String> getImports() {
return knownPackagePrefixes;
}
public void setClassLoader(ClassLoader loader) {
this.loader = loader;
}
}

Some files were not shown because too many files have changed in this diff Show More