Browse Source
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@5 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/head
115 changed files with 22313 additions and 0 deletions
@ -0,0 +1,68 @@
@@ -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> |
||||
* "Hello ${getName()}" |
||||
* </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); |
||||
} |
||||
} |
||||
@ -0,0 +1,24 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,24 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,54 @@
@@ -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()+"'"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1 @@
@@ -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;
} |
||||
@ -0,0 +1,69 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,54 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,40 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,120 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,312 @@
@@ -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)); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,76 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,46 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,75 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,157 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,41 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,156 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,54 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,83 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,67 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,88 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,64 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,65 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,63 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,323 @@
@@ -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()));
|
||||
} |
||||
} |
||||
@ -0,0 +1,36 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,70 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,65 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
@@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,109 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,63 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,60 @@
@@ -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()); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,55 @@
@@ -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())); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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 "<="; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,68 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,95 @@
@@ -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); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,63 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,87 @@
@@ -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 "*"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,51 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,63 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,88 @@
@@ -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(); |
||||
} |
||||
} |
||||
@ -0,0 +1,113 @@
@@ -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"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,46 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,98 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,239 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,69 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,34 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,88 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,146 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,110 @@
@@ -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(); |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,60 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,64 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,70 @@
@@ -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)); |
||||
} |
||||
} |
||||
@ -0,0 +1,375 @@
@@ -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' ; |
||||
@ -0,0 +1,92 @@
@@ -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 |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,174 @@
@@ -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' ; |
||||
@ -0,0 +1,39 @@
@@ -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() + "'>"; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,35 @@
@@ -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(); |
||||
} |
||||
} |
||||
@ -0,0 +1,33 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,220 @@
@@ -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); |
||||
} |
||||
} |
||||
@ -0,0 +1,81 @@
@@ -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]; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,78 @@
@@ -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]; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,103 @@
@@ -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; |
||||
} |
||||
} |
||||
@ -0,0 +1,76 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,57 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,35 @@
@@ -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(); |
||||
} |
||||
} |
||||
@ -0,0 +1,57 @@
@@ -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; |
||||
// }
|
||||
} |
||||
} |
||||
@ -0,0 +1,35 @@
@@ -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(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,54 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,35 @@
@@ -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; |
||||
|
||||
} |
||||
@ -0,0 +1,43 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,47 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,47 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,42 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
@@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,14 @@
@@ -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> |
||||
@ -0,0 +1,68 @@
@@ -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); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@
@@ -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); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,64 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,63 @@
@@ -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); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
@@ -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 + "'"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,21 @@
@@ -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() + "'"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,202 @@
@@ -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;
|
||||
// }
|
||||
|
||||
} |
||||
@ -0,0 +1,58 @@
@@ -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 + "'"); |
||||
} |
||||
} |
||||
@ -0,0 +1,612 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,78 @@
@@ -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()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,193 @@
@@ -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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,48 @@
@@ -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; |
||||
} |
||||
@ -0,0 +1,283 @@
@@ -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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,81 @@
@@ -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
Loading…
Reference in new issue