revised expression parser API design

This commit is contained in:
Juergen Hoeller
2009-02-12 23:03:58 +00:00
parent 2bdb62f4c2
commit 08dd18df58
147 changed files with 3053 additions and 4402 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,18 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
* An AccessException is thrown by an accessor if it has an unexpected problem.
*
* @author Andy Clement
* @since 3.0
*/
public class AccessException extends Exception {
/**
* Create an AccessException with a specific message and cause.
*
* @param message the message
* @param cause the cause
*/
@@ -34,7 +35,6 @@ public class AccessException extends Exception {
/**
* Create an AccessException with a specific message.
*
* @param message the message
*/
public AccessException(String message) {

View File

@@ -1,57 +0,0 @@
package org.springframework.expression;
/**
* A CacheablePropertyAccessor is an optimized PropertyAccessor where the two parts of accessing the property are
* separated: (1) resolving the property and (2) retrieving its value. In some cases there is a large cost to
* discovering which property an expression refers to and once discovered it will always resolve to the same property.
* In these situations a CacheablePropertyAccessor enables the resolution to be done once and a reusable object (an
* executor) returned that can be called over and over to retrieve the property value without going through resolution
* again.
* <p>
*
* @author Andy Clement
*/
public abstract class CacheablePropertyAccessor implements PropertyAccessor {
/**
* Attempt to resolve the named property and return an executor that can be called to get the value of that
* property. Return null if the property cannot be resolved.
*
* @param context the evaluation context
* @param target the target upon which the property is being accessed
* @param name the name of the property being accessed
* @return a reusable executor that can retrieve the property value
*/
public abstract PropertyReaderExecutor getReaderAccessor(EvaluationContext context, Object target, Object name);
/**
* Attempt to resolve the named property and return an executor that can be called to set the value of that
* property. Return null if the property cannot be resolved.
*
* @param context the evaluation context
* @param target the target upon which the property is being accessed
* @param name the name of the property to be set
* @return a reusable executor that can set the property value
*/
public abstract PropertyWriterExecutor getWriterAccessor(EvaluationContext context, Object target, Object name);
// Implementation of PropertyAccessor follows, based on the resolver/executor model
public final boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException {
return getReaderAccessor(context, target, name) != null;
}
public final boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException {
return getWriterAccessor(context, target, name) != null;
}
public final Object read(EvaluationContext context, Object target, Object name) throws AccessException {
return getReaderAccessor(context, target, name).execute(context, target);
}
public final void write(EvaluationContext context, Object target, Object name, Object newValue)
throws AccessException {
getWriterAccessor(context, target, name).execute(context, target, newValue);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
// TODO Is the resolver/executor model too pervasive in this package?
@@ -26,12 +27,12 @@ package org.springframework.expression;
* back to the resolvers to ask for a new one.
*
* @author Andy Clement
* @since 3.0
*/
public interface ConstructorExecutor {
/**
* Execute a constructor in the specified context using the specified arguments.
*
* @param context the evaluation context in which the command is being executed
* @param arguments the arguments to the constructor call, should match (in terms of number and type) whatever the
* command will need to run

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
@@ -20,6 +21,7 @@ package org.springframework.expression;
* that constructor. The ConstructorExecutor will be cached but if it 'goes stale' the resolvers will be called again.
*
* @author Andy Clement
* @since 3.0
*/
public interface ConstructorResolver {
@@ -27,13 +29,12 @@ public interface ConstructorResolver {
* Within the supplied context determine a suitable constructor on the supplied type that can handle the specified
* arguments. Return a ConstructorExecutor that can be used to invoke that constructor (or null if no constructor
* could be found).
*
* @param context the current evaluation context
* @param typename the type upon which to look for the constructor
* @param typeName the type upon which to look for the constructor
* @param argumentTypes the arguments that the constructor must be able to handle
* @return a ConstructorExecutor that can invoke the constructor, or null if non found
*/
ConstructorExecutor resolve(EvaluationContext context, String typename, Class<?>[] argumentTypes)
ConstructorExecutor resolve(EvaluationContext context, String typeName, Class<?>[] argumentTypes)
throws AccessException;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,61 +13,53 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
import java.util.List;
import org.springframework.expression.spel.standard.StandardEvaluationContext;
import org.springframework.expression.spel.standard.StandardTypeUtilities;
/**
* Expressions are executed in an evaluation context. It is in this context that references are resolved when
* encountered during expression evaluation.
* Expressions are executed in an evaluation context. It is in this context that references
* are resolved when encountered during expression evaluation.
*
* There is a default implementation of the EvaluationContext, {@link StandardEvaluationContext} that can be extended,
* rather than having to implement everything.
* There is a default implementation of the EvaluationContext,
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}
* that can be extended, rather than having to implement everything.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public interface EvaluationContext {
/**
* @return the root context object against which unqualified properties/methods/etc should be resolved
*/
Object getRootContextObject();
/**
* @return a TypeUtilities implementation that can be used for looking up types, converting types, comparing types,
* and overloading basic operators for types. A standard implementation is provided in {@link StandardTypeUtilities}
*/
TypeUtils getTypeUtils();
/**
* Look up a named variable within this execution context.
*
* @param name variable to lookup
* @return the value of the variable
*/
Object lookupVariable(String name);
Object getRootObject();
/**
* Set a named variable within this execution context to a specified value.
*
* @param name variable to set
* @param value value to be placed in the variable
*/
void setVariable(String name, Object value);
/**
* Look up a named variable within this execution context.
* @param name variable to lookup
* @return the value of the variable
*/
Object lookupVariable(String name);
// TODO lookupReference() - is it too expensive to return all objects within a context?
/**
* Look up an object reference in a particular context. If no contextName is specified (null), assume the default
* context. If no objectName is specified (null), return all objects in the specified context (List<Object>).
*
* @param contextName the context in which to perform the lookup (or null for default context)
* @param objectName the object to lookup in the context (or null to get all objects)
* @return a specific object or List<Object>
* context. If no objectName is specified (null), return all objects in the specified context (List).
* @param contextName the context in which to perform the lookup (or <code>null</code> for default context)
* @param objectName the object to lookup in the context (or <code>null</code> to get all objects)
* @return a specific object or List
*/
Object lookupReference(Object contextName, Object objectName) throws EvaluationException;
Object lookupReference(Object contextName, String objectName) throws EvaluationException;
/**
* @return a list of resolvers that will be asked in turn to locate a constructor
@@ -84,4 +76,25 @@ public interface EvaluationContext {
*/
List<PropertyAccessor> getPropertyAccessors();
/**
* @return a type locator that can be used to find types, either by short or fully qualified name.
*/
TypeLocator getTypeLocator();
/**
* @return a type comparator for comparing pairs of objects for equality.
*/
TypeComparator getTypeComparator();
/**
* @return a type converter that can convert (or coerce) a value from one type to another.
*/
TypeConverter getTypeConverter();
/**
* @return an operator overloader that may support mathematical operations between more than the standard set of
* types
*/
OperatorOverloader getOperatorOverloader();
}

View File

@@ -1,36 +1,34 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
* Base class for exceptions occurring during expression parsing and evaluation.
*
* @author Andy Clement
* @since 3.0
*/
public class EvaluationException extends Exception {
/**
* The expression string.
*/
private String expressionString;
/**
* Creates a new expression exception. The expressionString field should be set by a later call to
* setExpressionString().
*
* Creates a new expression exception.
* @param cause the underlying cause of this exception
*/
public EvaluationException(Throwable cause) {
@@ -39,7 +37,6 @@ public class EvaluationException extends Exception {
/**
* Creates a new expression parsing exception.
*
* @param expressionString the expression string that could not be parsed
* @param cause the underlying cause of this exception
*/
@@ -49,7 +46,6 @@ public class EvaluationException extends Exception {
/**
* Creates a new expression exception.
*
* @param expressionString the expression string
* @param message a descriptive message
* @param cause the underlying cause of this exception
@@ -61,7 +57,6 @@ public class EvaluationException extends Exception {
/**
* Creates a new expression exception.
*
* @param expressionString the expression string
* @param message a descriptive message
*/
@@ -73,27 +68,15 @@ public class EvaluationException extends Exception {
/**
* Creates a new expression exception. The expressionString field should be set by a later call to
* setExpressionString().
*
* @param message a descriptive message
*/
public EvaluationException(String message) {
super(message);
}
/**
* Set the expression string, called on exceptions where the expressionString is not known at the time of exception
* creation.
*
* @param expressionString the expression string
*/
protected final void setExpressionString(String expressionString) {
this.expressionString = expressionString;
public final String getExpressionString() {
return this.expressionString;
}
/**
* @return the expression string
*/
public final String getExpressionString() {
return expressionString;
}
}

View File

@@ -1,18 +1,19 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
@@ -22,6 +23,7 @@ package org.springframework.expression;
*
* @author Keith Donald
* @author Andy Clement
* @since 3.0
*/
public interface Expression {

View File

@@ -1,56 +1,56 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
* Parses expression strings into compiled expressions that can be evaluated. Supports parsing templates as well as
* standard expression strings.
*
* Parses expression strings into compiled expressions that can be evaluated.
* Supports parsing templates as well as standard expression strings.
*
* @author Keith Donald
* @author Andy Clement
* @since 3.0
*/
public interface ExpressionParser {
/**
* Parse the expression string and return an Expression object you can use for repeated evaluation. Some examples:
*
* Parse the expression string and return an Expression object you can use for repeated evaluation.
* <p>Some examples:
* <pre>
* 3 + 4
* name.firstName
* </pre>
* @param expressionString the raw expression string to parse
* @return an evaluator for the parsed expression
* @throws ParseException an exception occurred during parsing
*/
Expression parseExpression(String expressionString) throws ParseException;
/**
* Parse the expression string and return an Expression object you can use for repeated evaluation.
* <p>Some examples:
* <pre>
* 3 + 4
* name.firstName
* </pre>
*
* @param expressionString the raw expression string to parse
* @param context a context for influencing this expression parsing routine (optional)
* @return an evaluator for the parsed expression
* @throws ParseException an exception occurred during parsing
*/
public Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
/**
* Parse the expression string and return an Expression object you can use for repeated evaluation. Some examples:
*
* <pre>
* 3 + 4
* name.firstName
* </pre>
*
* @param expressionString the raw expression string to parse
* @return an evaluator for the parsed expression
* @throws ParseException an exception occurred during parsing
*/
public Expression parseExpression(String expressionString) throws ParseException;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
@@ -20,24 +21,24 @@ package org.springframework.expression;
* without going back to the resolvers. For example, the particular method to run on an object may be discovered by the
* reflection method resolver - it will then build a MethodExecutor that executes that method and the MethodExecutor can
* be reused without needing to go back to the resolver to discover the method again.
* <p>
* They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go
*
* <p>They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go
* back to the resolvers to ask for a new one.
*
*
* @author Andy Clement
* @since 3.0
*/
public interface MethodExecutor {
/**
* Execute a command using the specified arguments, and using the specified expression state.
*
* @param context the evaluation context in which the command is being executed
* @param target the target object of the call - null for static methods
* @param methodArguments the arguments to the executor, should match (in terms of number and type) whatever the
* @param arguments the arguments to the executor, should match (in terms of number and type) whatever the
* command will need to run
* @return the value returned from execution
* @throws AccessException if there is a problem executing the command or the MethodExecutor is no longer valid
*/
Object execute(EvaluationContext context, Object target, Object... methodArguments) throws AccessException;
Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
@@ -20,6 +21,7 @@ package org.springframework.expression;
* command executor will be cached but if it 'goes stale' the resolvers will be called again.
*
* @author Andy Clement
* @since 3.0
*/
public interface MethodResolver {
@@ -27,7 +29,6 @@ public interface MethodResolver {
* Within the supplied context determine a suitable method on the supplied object that can handle the specified
* arguments. Return a MethodExecutor that can be used to invoke that method (or null if no method
* could be found).
*
* @param context the current evaluation context
* @param targetObject the object upon which the method is being called
* @param argumentTypes the arguments that the constructor must be able to handle

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
package org.springframework.expression;
/**
* Supported operations that an {@link OperatorOverloader} can implement for any pair of operands.
*
* @author Andy Clement
* @since 3.0
*/
public enum Operation {
ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS;
ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS
}

View File

@@ -1,3 +1,19 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
@@ -5,6 +21,7 @@ package org.springframework.expression;
* implementation of OperatorOverloader, a user of the expression language can support these operations on other types.
*
* @author Andy Clement
* @since 3.0
*/
public interface OperatorOverloader {
@@ -12,26 +29,27 @@ public interface OperatorOverloader {
// TODO Operator overloading needs some testing!
/**
* Return true if the operator overloader supports the specified operation between the two operands and so should be
* invoked to handle it.
*
* Return true if the operator overloader supports the specified operation
* between the two operands and so should be invoked to handle it.
* @param operation the operation to be performed
* @param leftOperand the left operand
* @param rightOperand the right operand
* @return true if the OperatorOverloader supports the specified operation between the two operands
* @throws EvaluationException if there is a problem performing the operation
*/
boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException;
boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand)
throws EvaluationException;
/**
* Execute the specified operation on two operands, returning a result. See {@link Operation} for supported
* operations.
*
* Execute the specified operation on two operands, returning a result.
* See {@link Operation} for supported operations.
* @param operation the operation to be performed
* @param leftOperand the left operand
* @param rightOperand the right operand
* @return the result of performing the operation on the two operands
* @throws EvaluationException if there is a problem performing the operation
*/
Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException;
Object operate(Operation operation, Object leftOperand, Object rightOperand)
throws EvaluationException;
}

View File

@@ -1,36 +1,34 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
* Base class for exceptions occurring during expression parsing and evaluation.
*
*
* @author Andy Clement
* @since 3.0
*/
public class ParseException extends Exception {
/**
* The expression string.
*/
private String expressionString;
/**
* Creates a new expression exception. The expressionString field should be set by a later call to
* setExpressionString().
*
* Creates a new expression exception.
* @param cause the underlying cause of this exception
*/
public ParseException(Throwable cause) {
@@ -39,7 +37,6 @@ public class ParseException extends Exception {
/**
* Creates a new expression parsing exception.
*
* @param expressionString the expression string that could not be parsed
* @param cause the underlying cause of this exception
*/
@@ -49,7 +46,6 @@ public class ParseException extends Exception {
/**
* Creates a new expression exception.
*
* @param expressionString the expression string
* @param message a descriptive message
* @param cause the underlying cause of this exception
@@ -61,7 +57,6 @@ public class ParseException extends Exception {
/**
* Creates a new expression exception.
*
* @param expressionString the expression string
* @param message a descriptive message
*/
@@ -70,20 +65,9 @@ public class ParseException extends Exception {
this.expressionString = expressionString;
}
/**
* Set the expression string, called on exceptions where the expressionString is not known at the time of exception
* creation.
*
* @param expressionString the expression string
*/
protected final void setExpressionString(String expressionString) {
this.expressionString = expressionString;
public final String getExpressionString() {
return this.expressionString;
}
/**
* @return the expression string
*/
public final String getExpressionString() {
return expressionString;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2004-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
* Input provided to an expression parser that can influence an expression parsing/compilation routine.
*
*
* @author Keith Donald
* @author Andy Clement
* @since 3.0
*/
public interface ParserContext {
@@ -35,21 +37,22 @@ public interface ParserContext {
*
* @return true if the expression is a template, false otherwise
*/
public boolean isTemplate();
boolean isTemplate();
/**
* For template expressions, returns the prefix that identifies the start of an expression block within a string.
* For example "${"
*
* For example: "${"
*
* @return the prefix that identifies the start of an expression
*/
public String getExpressionPrefix();
String getExpressionPrefix();
/**
* For template expressions, return the prefix that identifies the end of an expression block within a string. For
* example "}$"
*
* For template expressions, return the prefix that identifies the end of an expression block within a string.
* For example: "}"
*
* @return the suffix that identifies the end of an expression
*/
public String getExpressionSuffix();
}
String getExpressionSuffix();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
@@ -23,66 +24,62 @@ package org.springframework.expression;
* to determine if it can read or write them. Property resolvers are considered to be ordered and each will be called in
* turn. The only rule that affects the call order is that any naming the target class directly in
* getSpecifiedTargetClasses() will be called first, before the general resolvers.
* <p>
* If the cost of locating the property is expensive, in relation to actually retrieving its value, consider extending
*
* <p>If the cost of locating the property is expensive, in relation to actually retrieving its value, consider extending
* CacheablePropertyAccessor rather than directly implementing PropertyAccessor. A CacheablePropertyAccessor enables the
* discovery (resolution) of the property to be done once and then an object (an executor) returned and cached by the
* infrastructure that can be used repeatedly to retrieve the property value.
*
* @author Andy Clement
* @since 3.0
*/
public interface PropertyAccessor {
/**
* Return an array of classes for which this resolver should be called. Returning null indicates this is a general
* resolver that can be called in an attempt to resolve a property on any type.
*
* @return an array of classes that this resolver is suitable for (or null if a general resolver)
*/
public Class[] getSpecificTargetClasses();
Class[] getSpecificTargetClasses();
/**
* Called to determine if a resolver instance is able to access a specified property on a specified target object.
*
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @return true if this resolver is able to read the property
* @throws AccessException if there is any problem determining whether the property can be read
*/
public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException;
boolean canRead(EvaluationContext context, Object target, String name) throws AccessException;
/**
* Called to read a property from a specified target object
*
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @return Object the value of the property
* @throws AccessException if there is any problem accessing the property value
*/
public Object read(EvaluationContext context, Object target, Object name) throws AccessException;
Object read(EvaluationContext context, Object target, String name) throws AccessException;
/**
* Called to determine if a resolver instance is able to write to a specified property on a specified target object.
*
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @return true if this resolver is able to write to the property
* @throws AccessException if there is any problem determining whether the property can be written to
*/
public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException;
boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException;
/**
* Called to write to a property on a specified target object. Should only succeed if canWrite() also returns true.
*
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @param newValue the new value for the property
* @throws AccessException if there is any problem writing to the property value
*/
public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException;
void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException;
}

View File

@@ -1,40 +0,0 @@
/*
* 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;
/**
* If a property accessor is built upon the CacheablePropertyAccessor class then once the property
* has been resolved the accessor will return an instance of this PropertyReaderExecutor interface
* that can be cached and repeatedly called to access the value of the property.
* <p>
* They can become stale, and in that case should throw an AccessException - this will cause the
* infrastructure to go back to the resolvers to ask for a new one.
*
* @author Andy Clement
*/
public interface PropertyReaderExecutor {
/**
* Return the value of a property for the specified target.
*
* @param context the evaluation context in which the command is being executed
* @param targetObject the target object on which property access is being attempted
* @return the property value
* @throws AccessException if there is a problem accessing the property or this executor has become stale
*/
Object execute(EvaluationContext context, Object targetObject) throws AccessException;
}

View File

@@ -1,40 +0,0 @@
/*
* 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;
/**
* If a property accessor is built upon the CacheablePropertyAccessor class then once the property
* has been resolved the accessor will return an instance of this PropertyWriterExecutor interface
* that can be cached and repeatedly called to set the value of the property.
*
* <p>They can become stale, and in that case should throw an AccessException - this will cause the
* infrastructure to go back to the resolvers to ask for a new one.
*
* @author Andy Clement
*/
public interface PropertyWriterExecutor {
/**
* Set the value of a property to the supplied new value.
* @param context the evaluation context in which the command is being executed
* @param targetObject the target object on which property write is being attempted
* @param newValue the new value for the property
* @throws AccessException if there is a problem setting the property or this executor has become stale
*/
void execute(EvaluationContext context, Object targetObject, Object newValue) throws AccessException;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
/**
@@ -20,12 +21,12 @@ package org.springframework.expression;
* return value is the same as for {@link Comparable}.
*
* @author Andy Clement
* @since 3.0
*/
public interface TypeComparator {
/**
* Compare two objects.
*
* @param firstObject the first object
* @param secondObject the second object
* @return 0 if they are equal, <0 if the first is smaller than the second, or >0 if the first is larger than the
@@ -36,11 +37,10 @@ public interface TypeComparator {
/**
* Return true if the comparator can compare these two objects
*
* @param firstObject the first object
* @param secondObject the second object
* @return true if the comparator can compare these objects
*/
public boolean canCompare(Object firstObject, Object secondObject);
boolean canCompare(Object firstObject, Object secondObject);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,18 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression;
import org.springframework.expression.spel.standard.StandardIndividualTypeConverter;
import org.springframework.expression.spel.standard.StandardTypeConverter;
/**
* A type converter can convert values between different types. There is a default implementation called
* {@link StandardTypeConverter} that supports some basic conversions. That default implementation can be extended
* through subclassing or via registration of new {@link StandardIndividualTypeConverter} instances with the
* StandardTypeConverter.
*
* A type converter can convert values between different types encountered
* during expression evaluation.
*
* @author Andy Clement
* @since 3.0
*/
public interface TypeConverter {
// TODO replace this stuff with Keiths spring-binding conversion code
@@ -32,21 +29,19 @@ public interface TypeConverter {
/**
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
*
* @param value the value to be converted
* @param targetType the type that the value should be converted to if possible
* @return the converted value
* @throws EvaluationException if conversion is not possible
*/
Object convertValue(Object value, Class<?> targetType) throws EvaluationException;
<T> T convertValue(Object value, Class<T> targetType) throws EvaluationException;
/**
* Return true if the type converter can convert the specified type to the desired target type.
*
* @param sourceType the type to be converted from
* @param targetType the type to be converted to
* @return true if that conversion can be performed
*/
public boolean canConvert(Class<?> sourceType, Class<?> targetType);
boolean canConvert(Class<?> sourceType, Class<?> targetType);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,14 +16,13 @@
package org.springframework.expression;
import org.springframework.expression.spel.standard.StandardTypeLocator;
/**
* Implementors of this interface are expected to be able to locate types. They may use custom classloaders or the
* and deal with common package prefixes (java.lang, etc) however they wish. See
* {@link StandardTypeLocator} for an example implementation.
* Implementors of this interface are expected to be able to locate types. They may use custom classloaders
* or the and deal with common package prefixes (java.lang, etc) however they wish. See
* {@link org.springframework.expression.spel.support.StandardTypeLocator} for an example implementation.
*
* @author Andy Clement
* @since 3.0
*/
public interface TypeLocator {

View File

@@ -1,54 +0,0 @@
/*
* 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;
/**
* TypeUtilities brings together the various kinds of type related function that may occur
* whilst working with expressions. An implementor is providing support for four type related
* facilities:
* <ul>
* <li>a mechanism for finding types
* <li>a mechanism for comparing types
* <li>a mechanism for type conversion/coercion
* <li>a mechanism for overloading mathematical operations (add/subtract/etc)
* </ul>
*
* @author Andy Clement
*/
public interface TypeUtils {
/**
* @return a type locator that can be used to find types, either by short or fully qualified name.
*/
TypeLocator getTypeLocator();
/**
* @return a type comparator for comparing pairs of objects for equality.
*/
TypeComparator getTypeComparator();
/**
* @return a type converter that can convert (or coerce) a value from one type to another.
*/
TypeConverter getTypeConverter();
/**
* @return an operator overloader that may support mathematical operations between more than the standard set of
* types
*/
OperatorOverloader getOperatorOverloader();
}

View File

@@ -1,78 +1,99 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.common;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.util.ObjectUtils;
/**
* Represents a template expression broken into pieces. Each piece will be an Expression but pure text parts to the
* template will be represented as LiteralExpression objects. An example of a template expression might be: <code><pre>
* &quot;Hello ${getName()}&quot;
* </pre></code> which will be represented as a CompositeStringExpression of two parts. The first part being a
* LiteralExpression representing 'Hello ' and the second part being a real expression that will call getName() when
* invoked.
* template will be represented as LiteralExpression objects. An example of a template expression might be:
*
* <pre class="code">
* &quot;Hello ${getName()}&quot;</pre>
*
* 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 <code>getName()</code> when invoked.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class CompositeStringExpression implements Expression {
private final String expressionString;
/**
* The array of expressions that make up the composite expression
*/
/** The array of expressions that make up the composite expression */
private final Expression[] expressions;
public CompositeStringExpression(String expressionString, Expression[] expressions) {
this.expressionString = expressionString;
this.expressions = expressions;
}
public String getExpressionString() {
return expressionString;
public final String getExpressionString() {
return this.expressionString;
}
public String getValue() throws EvaluationException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expressions.length; i++) {
// TODO is stringify ok for the non literal components? or should the converters be used? see another
// case below
sb.append(expressions[i].getValue());
for (Expression expression : this.expressions) {
sb.append(ObjectUtils.getDisplayString(expression.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));
for (Expression expression : this.expressions) {
sb.append(ObjectUtils.getDisplayString(expression.getValue(context)));
}
return sb.toString();
}
public Class getValueType(EvaluationContext context) throws EvaluationException {
public Class getValueType(EvaluationContext context) {
return String.class;
}
public Class getValueType() throws EvaluationException {
public Class getValueType() {
return String.class;
}
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
throw new EvaluationException(expressionString, "Cannot call setValue() on a composite expression");
throw new EvaluationException(this.expressionString, "Cannot call setValue on a composite expression");
}
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
Object value = getValue(context);
return (T)ExpressionUtils.convert(context, value, expectedResultType);
return ExpressionUtils.convert(context, value, expectedResultType);
}
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
Object value = getValue();
return (T)ExpressionUtils.convert(null, value, expectedResultType);
return ExpressionUtils.convert(null, value, expectedResultType);
}
public boolean isWritable(EvaluationContext context) throws EvaluationException {
public boolean isWritable(EvaluationContext context) {
return false;
}
}

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,43 +13,43 @@
* 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;
import org.springframework.util.ClassUtils;
/**
* Common utility functions that may be used by any Expression Language provider.
*
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class ExpressionUtils {
public abstract 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
* @param targetType 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 == null || toType.isAssignableFrom(value.getClass())) {
return value;
@SuppressWarnings("unchecked")
public static <T> T convert(EvaluationContext context, Object value, Class<T> targetType)
throws EvaluationException {
if (targetType == null || ClassUtils.isAssignableValue(targetType, value)) {
return (T) value;
}
if (context != null) {
TypeUtils typeUtils = context.getTypeUtils();
if (typeUtils != null) {
TypeConverter typeConverter = typeUtils.getTypeConverter();
return typeConverter.convertValue(value, toType);
}
return context.getTypeConverter().convertValue(value, targetType);
}
throw new EvaluationException("Cannot convert value '" + value + "' to type '" + toType.getName() + "'");
throw new EvaluationException("Cannot convert value '" + value + "' to type '" + targetType.getName() + "'");
}
}

View File

@@ -1,3 +1,19 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.common;
import org.springframework.expression.EvaluationContext;
@@ -8,35 +24,34 @@ 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
*
* @since 3.0
*/
public class LiteralExpression implements Expression {
/**
* Fixed literal value of this expression
*/
/** Fixed literal value of this expression */
private final String literalValue;
public LiteralExpression(String literalValue) {
this.literalValue = literalValue;
}
public String getExpressionString() {
return literalValue;
// return new StringBuilder().append("'").append(literalValue).append("'").toString();
public final String getExpressionString() {
return this.literalValue;
}
public String getValue() throws EvaluationException {
return literalValue;
public String getValue() {
return this.literalValue;
}
public String getValue(EvaluationContext context) throws EvaluationException {
return literalValue;
public String getValue(EvaluationContext context) {
return this.literalValue;
}
public Class getValueType(EvaluationContext context) throws EvaluationException {
public Class getValueType(EvaluationContext context) {
return String.class;
}
@@ -46,19 +61,19 @@ public class LiteralExpression implements Expression {
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
Object value = getValue(context);
return (T)ExpressionUtils.convert(context, value, expectedResultType);
return ExpressionUtils.convert(context, value, expectedResultType);
}
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
Object value = getValue();
return (T)ExpressionUtils.convert(null, value, expectedResultType);
return ExpressionUtils.convert(null, value, expectedResultType);
}
public boolean isWritable(EvaluationContext context) throws EvaluationException {
public boolean isWritable(EvaluationContext context) {
return false;
}
public Class getValueType() throws EvaluationException {
public Class getValueType() {
return String.class;
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.common;
import org.springframework.expression.ParserContext;
/**
* Configurable {@link ParserContext} implementation for template parsing.
* Expects the expression prefix and suffix as constructor arguments.
*
* @author Juergen Hoeller
* @since 3.0
*/
public class TemplateParserContext implements ParserContext {
private final String expressionPrefix;
private final String expressionSuffix;
/**
* Create a new TemplateParserContext for the given prefix and suffix.
* @param expressionPrefix the expression prefix to use
* @param expressionSuffix the expression suffix to use
*/
public TemplateParserContext(String expressionPrefix, String expressionSuffix) {
this.expressionPrefix = expressionPrefix;
this.expressionSuffix = expressionSuffix;
}
public final boolean isTemplate() {
return true;
}
public final String getExpressionPrefix() {
return this.expressionPrefix;
}
public final String getExpressionSuffix() {
return this.expressionSuffix;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,6 @@ import org.springframework.expression.Operation;
import org.springframework.expression.OperatorOverloader;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypeUtils;
/**
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other
@@ -38,203 +36,158 @@ import org.springframework.expression.TypeUtils;
* It also acts as a place for to define common utility routines that the various Ast nodes might need.
*
* @author Andy Clement
* @since 3.0
*/
public class ExpressionState {
private EvaluationContext relatedContext;
private final EvaluationContext relatedContext;
private final Stack<VariableScope> variableScopes = new Stack<VariableScope>();
private final Stack<Object> contextObjects = new Stack<Object>();
public ExpressionState(EvaluationContext context) {
relatedContext = context;
createVariableScope();
}
public ExpressionState() {
this(null);
}
public ExpressionState(EvaluationContext context) {
this.relatedContext = context;
createVariableScope();
}
private void createVariableScope() {
variableScopes.add(new VariableScope()); // create an empty top level VariableScope
this.variableScopes.add(new VariableScope()); // create an empty top level VariableScope
}
/**
* The active context object is what unqualified references to properties/etc are resolved against.
*/
public Object getActiveContextObject() {
if (contextObjects.isEmpty()) {
return relatedContext.getRootContextObject();
if (this.contextObjects.isEmpty()) {
return this.relatedContext.getRootObject();
}
return contextObjects.peek();
return this.contextObjects.peek();
}
public void pushActiveContextObject(Object obj) {
contextObjects.push(obj);
this.contextObjects.push(obj);
}
public void popActiveContextObject() {
contextObjects.pop();
this.contextObjects.pop();
}
public Object getRootContextObject() {
return relatedContext.getRootContextObject();
}
public Object lookupReference(Object contextName, Object objectName) throws EvaluationException {
return relatedContext.lookupReference(contextName, objectName);
}
public TypeUtils getTypeUtilities() {
return relatedContext.getTypeUtils();
}
public TypeComparator getTypeComparator() {
return relatedContext.getTypeUtils().getTypeComparator();
}
public Class<?> findType(String type) throws EvaluationException {
return getTypeUtilities().getTypeLocator().findType(type);
}
// TODO all these methods that grab the type converter will fail badly if there isn't one...
public boolean toBoolean(Object value) throws EvaluationException {
return ((Boolean) getTypeConverter().convertValue(value, Boolean.TYPE)).booleanValue();
}
public char toCharacter(Object value) throws EvaluationException {
return ((Character) getTypeConverter().convertValue(value, Character.TYPE)).charValue();
}
public short toShort(Object value) throws EvaluationException {
return ((Short) getTypeConverter().convertValue(value, Short.TYPE)).shortValue();
}
public int toInteger(Object value) throws EvaluationException {
return ((Integer) getTypeConverter().convertValue(value, Integer.TYPE)).intValue();
}
public double toDouble(Object value) throws EvaluationException {
return ((Double) getTypeConverter().convertValue(value, Double.TYPE)).doubleValue();
}
public float toFloat(Object value) throws EvaluationException {
return ((Float) getTypeConverter().convertValue(value, Float.TYPE)).floatValue();
}
public long toLong(Object value) throws EvaluationException {
return ((Long) getTypeConverter().convertValue(value, Long.TYPE)).longValue();
}
public byte toByte(Object value) throws EvaluationException {
return ((Byte) getTypeConverter().convertValue(value, Byte.TYPE)).byteValue();
}
public TypeConverter getTypeConverter() {
// TODO cache TypeConverter when it is set/changed?
return getTypeUtilities().getTypeConverter();
return this.relatedContext.getRootObject();
}
public void setVariable(String name, Object value) {
relatedContext.setVariable(name, value);
this.relatedContext.setVariable(name, value);
}
public Object lookupVariable(String name) {
return relatedContext.lookupVariable(name);
return this.relatedContext.lookupVariable(name);
}
public Object lookupReference(Object contextName, String objectName) throws EvaluationException {
return this.relatedContext.lookupReference(contextName, objectName);
}
public TypeComparator getTypeComparator() {
return this.relatedContext.getTypeComparator();
}
public Class<?> findType(String type) throws EvaluationException {
return this.relatedContext.getTypeLocator().findType(type);
}
public <T> T convertValue(Object value, Class<T> targetType) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value, targetType);
}
/**
* A new scope is entered when a function is invoked
*/
public void enterScope(Map<String, Object> argMap) {
variableScopes.push(new VariableScope(argMap));
this.variableScopes.push(new VariableScope(argMap));
}
public void enterScope(String name, Object value) {
variableScopes.push(new VariableScope(name, value));
this.variableScopes.push(new VariableScope(name, value));
}
public void exitScope() {
variableScopes.pop();
this.variableScopes.pop();
}
public void setLocalVariable(String name, Object value) {
variableScopes.peek().setVariable(name, value);
this.variableScopes.peek().setVariable(name, value);
}
public Object lookupLocalVariable(String name) {
int scopeNumber = variableScopes.size() - 1;
int scopeNumber = this.variableScopes.size() - 1;
for (int i = scopeNumber; i >= 0; i--) {
if (variableScopes.get(i).definesVariable(name)) {
return variableScopes.get(i).lookupVariable(name);
if (this.variableScopes.get(i).definesVariable(name)) {
return this.variableScopes.get(i).lookupVariable(name);
}
}
return null;
}
public Object operate(Operation op, Object left, Object right) throws SpelException {
OperatorOverloader overloader = relatedContext.getTypeUtils().getOperatorOverloader();
try {
if (overloader != null && overloader.overridesOperation(op, left, right)) {
return overloader.operate(op, left, right);
} else {
throw new SpelException(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, left, right);
}
} catch (EvaluationException e) {
if (e instanceof SpelException) {
throw (SpelException) e;
} else {
throw new SpelException(e, SpelMessages.UNEXPECTED_PROBLEM_INVOKING_OPERATOR, op, left, right, e
.getMessage());
}
public Object operate(Operation op, Object left, Object right) throws EvaluationException {
OperatorOverloader overloader = this.relatedContext.getOperatorOverloader();
if (overloader.overridesOperation(op, left, right)) {
return overloader.operate(op, left, right);
}
else {
throw new SpelException(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, left, right);
}
}
public List<PropertyAccessor> getPropertyAccessors() {
return relatedContext.getPropertyAccessors();
return this.relatedContext.getPropertyAccessors();
}
public EvaluationContext getEvaluationContext() {
return relatedContext;
return this.relatedContext;
}
/**
* 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
*
*/
static class VariableScope {
private static 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);
if (arguments != null) {
this.vars.putAll(arguments);
}
}
public VariableScope(String name,Object value) {
vars.put(name,value);
public VariableScope(String name, Object value) {
this.vars.put(name,value);
}
public Object lookupVariable(String name) {
return vars.get(name);
return this.vars.get(name);
}
public void setVariable(String name, Object value) {
vars.put(name,value);
this.vars.put(name,value);
}
public boolean definesVariable(String name) {
return vars.containsKey(name);
return this.vars.containsKey(name);
}
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.expression.EvaluationException;
* that can occur.
*
* @author Andy Clement
*
* @since 3.0
*/
public class SpelException extends EvaluationException {

View File

@@ -26,18 +26,17 @@ import org.springframework.expression.common.ExpressionUtils;
* asked to resolve references to types, beans, properties, methods.
*
* @author Andy Clement
*
* @since 3.0
*/
public class SpelExpression implements Expression {
private final String expression;
public final SpelNode ast;
/**
* Construct an expression, only used by the parser.
*
* @param expression
* @param ast
*/
public SpelExpression(String expression, SpelNode ast) {
this.expression = expression;
@@ -48,26 +47,27 @@ public class SpelExpression implements Expression {
* @return the expression string that was parsed to create this expression instance
*/
public String getExpressionString() {
return expression;
return this.expression;
}
/**
* {@inheritDoc}
*/
public Object getValue() throws EvaluationException {
return ast.getValue(null);
return this.ast.getValue(null);
}
/**
* {@inheritDoc}
*/
public Object getValue(EvaluationContext context) throws EvaluationException {
return ast.getValue(new ExpressionState(context));
return this.ast.getValue(new ExpressionState(context));
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
Object result = ast.getValue(new ExpressionState(context));
@@ -75,31 +75,31 @@ public class SpelExpression implements Expression {
Class<?> resultType = result.getClass();
if (!expectedResultType.isAssignableFrom(resultType)) {
// Attempt conversion to the requested type, may throw an exception
result = context.getTypeUtils().getTypeConverter().convertValue(result, expectedResultType);
result = context.getTypeConverter().convertValue(result, expectedResultType);
}
}
return (T)result;
return (T) result;
}
/**
* {@inheritDoc}
*/
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
ast.setValue(new ExpressionState(context), value);
this.ast.setValue(new ExpressionState(context), value);
}
/**
* {@inheritDoc}
*/
public boolean isWritable(EvaluationContext context) throws EvaluationException {
return ast.isWritable(new ExpressionState(context));
return this.ast.isWritable(new ExpressionState(context));
}
/**
* @return return the Abstract Syntax Tree for the expression
*/
public SpelNode getAST() {
return ast;
return this.ast;
}
/**
@@ -110,7 +110,7 @@ public class SpelExpression implements Expression {
* @return the string representation of the AST
*/
public String toStringAST() {
return ast.toStringAST();
return this.ast.toStringAST();
}
/**
@@ -120,11 +120,7 @@ public class SpelExpression implements Expression {
// TODO is this a legal implementation? The null return value could be very unhelpful. See other getValueType()
// also.
Object value = getValue(context);
if (value == null) {
return null;
} else {
return value.getClass();
}
return (value != null ? value.getClass() : null);
}
/**
@@ -132,20 +128,17 @@ public class SpelExpression implements Expression {
*/
public Class getValueType() throws EvaluationException {
Object value = getValue();
if (value == null) {
return null;
} else {
return value.getClass();
}
return (value != null ? value.getClass() : null);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
Object result = getValue();
// TODO propagate generic-ness into convert
return (T)ExpressionUtils.convert(null, result, expectedResultType);
return (T) ExpressionUtils.convert(null, result, expectedResultType);
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.DefaultNonTemplateParserContext;
import org.springframework.expression.common.TemplateAwareExpressionParser;
import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser;
/**
* Instances of this parser class can process Spring Expression Language format expressions. The result of parsing an
* expression is a SpelExpression instance that can be repeatedly evaluated (possibly against different evaluation
* contexts) or serialized for later evaluation.
*
* @author Andy Clement
*/
public class SpelExpressionParser extends TemplateAwareExpressionParser {
private final SpelInternalParser expressionParser;
public SpelExpressionParser() {
// Use an Antlr based expression parser
expressionParser = new SpelAntlrExpressionParser();
}
/**
* Parse an expression string.
*
* @param expressionString the expression to parse
* @param context the parser context in which to perform the parse
* @return a parsed expression object
* @throws ParseException if the expression is invalid
*/
@Override
protected Expression doParseExpression(String expressionString, ParserContext context) throws ParseException {
return expressionParser.doParseExpression(expressionString,context);
}
/**
* Simple override with covariance to return a nicer type
*/
@Override
public SpelExpression parseExpression(String expressionString) throws ParseException {
return (SpelExpression) super.parseExpression(expressionString, DefaultNonTemplateParserContext.INSTANCE);
}
public interface SpelInternalParser {
Expression doParseExpression(String expressionString, ParserContext context) throws ParseException;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel;
import java.text.MessageFormat;
@@ -33,7 +34,7 @@ import java.text.MessageFormat;
* message has had all relevant inserts applied to it.
*
* @author Andy Clement
*
* @since 3.0
*/
public enum SpelMessages {
// TODO put keys and messages into bundles for easy NLS
@@ -107,7 +108,7 @@ public enum SpelMessages {
Kind.ERROR, 1060, "Expected the type of the new array to be specified as a String but found ''{0}''"), PROBLEM_DURING_TYPE_CONVERSION(
Kind.ERROR, 1061, "Problem occurred during type conversion: {0}"), MULTIPLE_POSSIBLE_METHODS(Kind.ERROR,
1062, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"), EXCEPTION_DURING_PROPERTY_WRITE(
Kind.ERROR, 1063, "A problem occurred whilst attempting to set the property ''{0}'': ''{1}''"), NOT_AN_INTEGER(
Kind.ERROR, 1063, "A problem occurred whilst attempting to set the property ''{0}'': {1}"), NOT_AN_INTEGER(
Kind.ERROR, 1064, "The value ''{0}'' cannot be parsed as an int"), NOT_A_LONG(Kind.ERROR, 1065,
"The value ''{0}'' cannot be parsed as a long"), PARSE_PROBLEM(Kind.ERROR, 1066,
"Error occurred during expression parse: {0}"), INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR,
@@ -121,9 +122,6 @@ public enum SpelMessages {
private int code;
private String message;
public static enum Kind {
INFO, WARNING, ERROR
};
private SpelMessages(Kind kind, int code, String message) {
this.kind = kind;
@@ -131,6 +129,7 @@ public enum SpelMessages {
this.message = message;
}
/**
* Produce a complete message including the prefix, the position (if known) and with the inserts applied to the
* message.
@@ -160,4 +159,10 @@ public enum SpelMessages {
formattedMessage.append(MessageFormat.format(message, inserts));
return formattedMessage.toString();
}
public static enum Kind {
INFO, WARNING, ERROR
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,20 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel;
import org.springframework.expression.EvaluationException;
/**
* Represents a node in the Ast for a parsed expression.
*
*
* @author Andy Clement
* @since 3.0
*/
public interface SpelNode {
/**
* Evaluate the expression node in the context of the supplied expression state and return the value.
*
* @param expressionState the current expression state (includes the context)
* @return the value of this node evaluated against the specified state
*/
@@ -44,7 +45,6 @@ public interface SpelNode {
/**
* 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
@@ -60,26 +60,23 @@ public interface SpelNode {
* @return the number of children under this node
*/
int getChildCount();
/**
* Helper method that returns a SpelNode rather than an Antlr Tree node.
*
* @return the child node cast to a SpelNode
*/
SpelNode getChild(int index);
/**
* Determine the class of the object passed in, unless it is already a class object.
*
* @param o the object that the caller wants the class of
* @return the class of the object if it is not already a class object, or null if the object is null
*/
Class<?> getObjectClass(Object o);
Class<?> getObjectClass(Object obj);
/**
* @return the start position of this Ast node in the expression string
*/
public int getStartPosition();
int getStartPosition();
}
}

View File

@@ -22,9 +22,9 @@ package org.springframework.expression.spel;
*
* @author Andy Clement
*/
public class WrappedELException extends RuntimeException {
public class WrappedSpelException extends RuntimeException {
public WrappedELException(SpelException e) {
public WrappedSpelException(SpelException e) {
super(e);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,63 +13,70 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.antlr;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateAwareExpressionParser;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelExpression;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.WrappedELException;
import org.springframework.expression.spel.SpelExpressionParser.SpelInternalParser;
import org.springframework.expression.spel.WrappedSpelException;
import org.springframework.expression.spel.generated.SpringExpressionsLexer;
import org.springframework.expression.spel.generated.SpringExpressionsParser.expr_return;
/**
* Wrap an Antlr lexer and parser.
*
* Default {@link org.springframework.expression.ExpressionParser} implementation,
* wrapping an Antlr lexer and parser that implements standard Spring EL syntax.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class SpelAntlrExpressionParser implements SpelInternalParser {
public class SpelAntlrExpressionParser extends TemplateAwareExpressionParser {
private final SpringExpressionsLexer lexer;
private final SpringExpressionsParserExtender parser;
public SpelAntlrExpressionParser() {
lexer = new SpringExpressionsLexerExtender();
CommonTokenStream tokens = new CommonTokenStream(lexer);
parser = new SpringExpressionsParserExtender(tokens);
this.lexer = new SpringExpressionsLexerExtender();
CommonTokenStream tokens = new CommonTokenStream(this.lexer);
this.parser = new SpringExpressionsParserExtender(tokens);
}
/**
* Parse an expression string.
*
* @param expressionString the expression to parse
* @param context the parser context in which to perform the parse
* @return a parsed expression object
* @throws ParseException if the expression is invalid
*/
public Expression doParseExpression(String expressionString, ParserContext context) throws ParseException {
protected Expression doParseExpression(String expressionString, ParserContext context) throws ParseException {
try {
lexer.setCharStream(new ANTLRStringStream(expressionString));
CommonTokenStream tokens = new CommonTokenStream(lexer);
parser.setTokenStream(tokens);
expr_return exprReturn = parser.expr();
SpelExpression newExpression = new SpelExpression(expressionString, (SpelNode) exprReturn.getTree());
return newExpression;
} catch (RecognitionException re) {
ParseException exception = new ParseException(expressionString, "Recognition error at position: "
+ re.charPositionInLine + ": " + re.getMessage(), re);
throw exception;
} catch (WrappedELException e) {
SpelException wrappedException = e.getCause();
throw new ParseException(expressionString, "Parsing problem: " + wrappedException.getMessage(),
wrappedException);
this.lexer.setCharStream(new ANTLRStringStream(expressionString));
CommonTokenStream tokens = new CommonTokenStream(this.lexer);
this.parser.setTokenStream(tokens);
expr_return exprReturn = this.parser.expr();
return new SpelExpression(expressionString, (SpelNode) exprReturn.getTree());
}
catch (RecognitionException re) {
throw new ParseException(expressionString,
"Recognition error at position: " + re.charPositionInLine + ": " + re.getMessage(), re);
}
catch (WrappedSpelException ex) {
SpelException wrappedException = ex.getCause();
throw new ParseException(expressionString,
"Parsing problem: " + wrappedException.getMessage(), wrappedException);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,19 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.antlr;
import org.antlr.runtime.RecognitionException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.WrappedELException;
import org.springframework.expression.spel.WrappedSpelException;
import org.springframework.expression.spel.generated.SpringExpressionsLexer;
public class SpringExpressionsLexerExtender extends SpringExpressionsLexer {
public SpringExpressionsLexerExtender() {
super();
}
/**
* @author Andy Clement
* @since 3.0
*/
class SpringExpressionsLexerExtender extends SpringExpressionsLexer {
/**
* recover() attempts to provide better error messages once something has gone wrong. It then throws a
@@ -64,7 +65,7 @@ public class SpringExpressionsLexerExtender extends SpringExpressionsLexer {
// getCharErrorDisplay(mte.expecting), getCharErrorDisplay(mte.c));
// }
SpelException realException = new SpelException(re, SpelMessages.RECOGNITION_ERROR, re.toString());
throw new WrappedELException(realException);
throw new WrappedSpelException(realException);
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.antlr;
import org.antlr.runtime.BitSet;
@@ -22,11 +23,15 @@ import org.antlr.runtime.Token;
import org.antlr.runtime.TokenStream;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.WrappedELException;
import org.springframework.expression.spel.WrappedSpelException;
import org.springframework.expression.spel.ast.SpelTreeAdaptor;
import org.springframework.expression.spel.generated.SpringExpressionsParser;
public class SpringExpressionsParserExtender extends SpringExpressionsParser {
/**
* @author Andy Clement
* @since 3.0
*/
class SpringExpressionsParserExtender extends SpringExpressionsParser {
public SpringExpressionsParserExtender(TokenStream input) {
super(input);
@@ -53,7 +58,7 @@ public class SpringExpressionsParserExtender extends SpringExpressionsParser {
// message = "no more input data to process whilst constructing " + paraphrase.peek();
// }
SpelException parsingProblem = new SpelException(e.charPositionInLine, e, SpelMessages.PARSE_PROBLEM, message);
throw new WrappedELException(parsingProblem);
throw new WrappedSpelException(parsingProblem);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -22,11 +23,11 @@ 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'
*
*
* <p>Example: 'someNumberProperty=42'
*
* @author Andy Clement
*
* @since 3.0
*/
public class Assign extends SpelNodeImpl {
@@ -51,4 +52,5 @@ public class Assign extends SpelNodeImpl {
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,15 +13,16 @@
* 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
*
* @since 3.0
*/
public class BooleanLiteral extends Literal {
@@ -34,7 +35,7 @@ public class BooleanLiteral extends Literal {
@Override
public Boolean getLiteralValue() {
return value;
return this.value;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -24,7 +25,7 @@ import org.springframework.expression.spel.ExpressionState;
* Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'
*
* @author Andy Clement
*
* @since 3.0
*/
public class CompoundExpression extends SpelNodeImpl {
@@ -35,7 +36,6 @@ public class CompoundExpression extends SpelNodeImpl {
/**
* 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
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,12 +13,14 @@
* 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;
@@ -27,8 +29,6 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
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
@@ -40,41 +40,38 @@ import org.springframework.expression.spel.internal.Utils;
* new int[3] new int[3]{1,2,3}
*
* @author Andy Clement
*
* @author Juergen Hoeller
* @since 3.0
*/
public class ConstructorReference extends SpelNodeImpl {
/**
* 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;
/**
* The cached executor that may be reused on subsequent evaluations.
*/
private ConstructorExecutor cachedExecutor;
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 getValueInternal(ExpressionState state) throws EvaluationException {
if (isArrayConstructor) {
if (this.isArrayConstructor) {
return createArray(state);
} else {
}
else {
return createNewInstance(state);
}
}
@@ -87,7 +84,7 @@ public class ConstructorReference extends SpelNodeImpl {
int index = 0;
sb.append(getChild(index++).toStringAST());
if (!isArrayConstructor) {
if (!this.isArrayConstructor) {
sb.append("(");
for (int i = index; i < getChildCount(); i++) {
if (i > index)
@@ -95,7 +92,8 @@ public class ConstructorReference extends SpelNodeImpl {
sb.append(getChild(i).toStringAST());
}
sb.append(")");
} else {
}
else {
// Next child is EXPRESSIONLIST token with children that are the
// expressions giving array size
sb.append("[");
@@ -117,10 +115,10 @@ public class ConstructorReference extends SpelNodeImpl {
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
@@ -129,8 +127,8 @@ public class ConstructorReference extends SpelNodeImpl {
Object intendedArrayType = getChild(0).getValueInternal(state);
if (!(intendedArrayType instanceof String)) {
throw new SpelException(getChild(0).getCharPositionInLine(),
SpelMessages.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION, Utils
.formatClassnameForMessage(intendedArrayType.getClass()));
SpelMessages.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION,
FormatHelper.formatClassNameForMessage(intendedArrayType.getClass()));
}
String type = (String) intendedArrayType;
Class<?> componentType = null;
@@ -151,12 +149,13 @@ public class ConstructorReference extends SpelNodeImpl {
// 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 {
}
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).getValueInternal(state);
int arraySize = state.toInteger(o);
int arraySize = state.convertValue(o, Integer.class);
if (getChildCount() == 3) {
// Check initializer length matches array size length
int initializerLength = getChild(2).getChildCount();
@@ -166,11 +165,12 @@ public class ConstructorReference extends SpelNodeImpl {
}
}
newArray = Array.newInstance(componentType, arraySize);
} else {
}
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).getValueInternal(state));
dims[d] = state.convertValue(getChild(1).getChild(d).getValueInternal(state), Integer.class);
}
newArray = Array.newInstance(componentType, dims);
// TODO check any specified initializer for the multidim array matches
@@ -195,42 +195,42 @@ public class ConstructorReference extends SpelNodeImpl {
} else if (arrayTypeCode == TypeCode.INT) {
int[] newIntArray = (int[]) newArray;
for (int i = 0; i < newIntArray.length; i++) {
newIntArray[i] = state.toInteger(initializer.getChild(i).getValueInternal(state));
newIntArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Integer.class);
}
} else if (arrayTypeCode == TypeCode.BOOLEAN) {
boolean[] newBooleanArray = (boolean[]) newArray;
for (int i = 0; i < newBooleanArray.length; i++) {
newBooleanArray[i] = state.toBoolean(initializer.getChild(i).getValueInternal(state));
newBooleanArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Boolean.class);
}
} else if (arrayTypeCode == TypeCode.CHAR) {
char[] newCharArray = (char[]) newArray;
for (int i = 0; i < newCharArray.length; i++) {
newCharArray[i] = state.toCharacter(initializer.getChild(i).getValueInternal(state));
newCharArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Character.class);
}
} else if (arrayTypeCode == TypeCode.SHORT) {
short[] newShortArray = (short[]) newArray;
for (int i = 0; i < newShortArray.length; i++) {
newShortArray[i] = state.toShort(initializer.getChild(i).getValueInternal(state));
newShortArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Short.class);
}
} else if (arrayTypeCode == TypeCode.LONG) {
long[] newLongArray = (long[]) newArray;
for (int i = 0; i < newLongArray.length; i++) {
newLongArray[i] = state.toLong(initializer.getChild(i).getValueInternal(state));
newLongArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Long.class);
}
} else if (arrayTypeCode == TypeCode.FLOAT) {
float[] newFloatArray = (float[]) newArray;
for (int i = 0; i < newFloatArray.length; i++) {
newFloatArray[i] = state.toFloat(initializer.getChild(i).getValueInternal(state));
newFloatArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Float.class);
}
} else if (arrayTypeCode == TypeCode.DOUBLE) {
double[] newDoubleArray = (double[]) newArray;
for (int i = 0; i < newDoubleArray.length; i++) {
newDoubleArray[i] = state.toDouble(initializer.getChild(i).getValueInternal(state));
newDoubleArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Double.class);
}
} else if (arrayTypeCode == TypeCode.BYTE) {
byte[] newByteArray = (byte[]) newArray;
for (int i = 0; i < newByteArray.length; i++) {
newByteArray[i] = state.toByte(initializer.getChild(i).getValueInternal(state));
newByteArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Byte.class);
}
}
}
@@ -240,7 +240,6 @@ public class ConstructorReference extends SpelNodeImpl {
/**
* 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
@@ -254,41 +253,41 @@ public class ConstructorReference extends SpelNodeImpl {
argumentTypes[i] = childValue.getClass();
}
if (cachedExecutor != null) {
ConstructorExecutor executorToUse = this.cachedExecutor;
if (executorToUse != null) {
try {
return cachedExecutor.execute(state.getEvaluationContext(), arguments);
} catch (AccessException ae) {
return executorToUse.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
this.cachedExecutor = null;
}
}
// either there was no accessor or it no longer exists
String typename = (String) getChild(0).getValueInternal(state);
cachedExecutor = findExecutorForConstructor(typename, argumentTypes, state);
executorToUse = findExecutorForConstructor(typename, argumentTypes, state);
try {
return cachedExecutor.execute(state.getEvaluationContext(), arguments);
} catch (AccessException ae) {
return executorToUse.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 {
protected ConstructorExecutor findExecutorForConstructor(
String typename, Class<?>[] argumentTypes, ExpressionState state) throws SpelException {
EvaluationContext eContext = state.getEvaluationContext();
List<ConstructorResolver> cResolvers = eContext.getConstructorResolvers();
if (cResolvers != null) {
@@ -299,18 +298,14 @@ public class ConstructorReference extends SpelNodeImpl {
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));
}
}
catch (AccessException ex) {
throw new SpelException(ex, SpelMessages.PROBLEM_LOCATING_CONSTRUCTOR, typename,
FormatHelper.formatMethodForMessage("", argumentTypes));
}
}
}
throw new SpelException(SpelMessages.CONSTRUCTOR_NOT_FOUND, typename, Utils.formatMethodForMessage("",
throw new SpelException(SpelMessages.CONSTRUCTOR_NOT_FOUND, typename, FormatHelper.formatMethodForMessage("",
argumentTypes));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -21,9 +22,9 @@ import org.springframework.expression.spel.ExpressionState;
/**
* This is used for preserving positional information from the input expression.
*
*
* @author Andy Clement
*
* @since 3.0
*/
public class Dot extends SpelNodeImpl {
// TODO Keep Dot for the positional information or remove it?

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,12 +13,17 @@
* 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;
/**
* @author Andy Clement
* @since 3.0
*/
public class EmptySpelNode extends SpelNodeImpl {
public EmptySpelNode(Token payload) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.internal;
package org.springframework.expression.spel.ast;
/**
* Utility methods (formatters, etc) used during parsing and evaluation.
*
* @author Andy Clement
*/
public class Utils {
class FormatHelper {
/**
* 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)
@@ -35,8 +35,9 @@ public class Utils {
sb.append("(");
if (argumentTypes != null) {
for (int i = 0; i < argumentTypes.length; i++) {
if (i > 0)
if (i > 0) {
sb.append(",");
}
sb.append(argumentTypes[i].getName());
}
}
@@ -47,12 +48,11 @@ public class Utils {
/**
* 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) {
public static String formatClassNameForMessage(Class<?> clazz) {
if (clazz == null) {
return "null";
}
StringBuilder fmtd = new StringBuilder();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -20,13 +21,13 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.reflection.ReflectionUtils;
import org.springframework.expression.spel.support.ReflectionHelper;
/**
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in the context prior to the
@@ -38,16 +39,19 @@ import org.springframework.expression.spel.reflection.ReflectionUtils;
* Functions are very simplistic, the arguments are not part of the definition (right now), so the names must be unique.
*
* @author Andy Clement
* @since 3.0
*/
public class FunctionReference extends SpelNodeImpl {
private final String name;
public FunctionReference(Token payload) {
super(payload);
name = payload.getText();
this.name = payload.getText();
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object o = state.lookupVariable(name);
@@ -87,15 +91,11 @@ public class FunctionReference extends SpelNodeImpl {
// Convert arguments if necessary and remap them for varargs if required
if (functionArgs != null) {
EvaluationContext ctx = state.getEvaluationContext();
TypeConverter converter = null;
if (ctx.getTypeUtils() != null) {
converter = ctx.getTypeUtils().getTypeConverter();
}
ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, functionArgs);
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
ReflectionHelper.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, functionArgs);
}
if (m.isVarArgs()) {
functionArgs = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), functionArgs);
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(m.getParameterTypes(), functionArgs);
}
try {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,29 +13,35 @@
* 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;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
/**
* @author Andy Clement
* @since 3.0
*/
public class Identifier extends SpelNodeImpl {
private final String id;
public Identifier(Token payload) {
super(payload);
id = payload.getText();
this.id = payload.getText();
}
@Override
public String toStringAST() {
return id;
return this.id;
}
@Override
public String getValueInternal(ExpressionState state) throws SpelException {
return id;
public String getValueInternal(ExpressionState state) {
return this.id;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.Collection;
@@ -31,6 +32,7 @@ import org.springframework.expression.spel.SpelMessages;
* strings/collections (lists/sets)/arrays
*
* @author Andy Clement
* @since 3.0
*/
public class Indexer extends SpelNodeImpl {
@@ -48,7 +50,7 @@ public class Indexer extends SpelNodeImpl {
return ((Map<?, ?>) ctx).get(index);
}
int idx = state.toInteger(index);
int idx = state.convertValue(index, Integer.class);
if (ctx.getClass().isArray()) {
return accessArrayElement(ctx, idx);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,16 @@
* 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;
/**
* Expression language AST node that represents an integer literal.
*
*
* @author Andy Clement
* @since 3.0
*/
public class IntLiteral extends Literal {
@@ -33,7 +35,7 @@ public class IntLiteral extends Literal {
@Override
public Integer getLiteralValue() {
return value;
return this.value;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.internal;
package org.springframework.expression.spel.ast;
/**
* Special object that is used to wrap a map entry/value when iterating over a map. Providing a direct way for the
@@ -21,7 +22,8 @@ package org.springframework.expression.spel.internal;
*
* @author Andy Clement
*/
public class KeyValuePair {
class KeyValuePair {
public Object key;
public Object value;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,14 @@
* 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.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.WrappedELException;
import org.springframework.expression.spel.WrappedSpelException;
/**
* Common superclass for nodes representing literals (boolean, string, number, etc).
@@ -84,7 +85,7 @@ public abstract class Literal extends SpelNodeImpl {
long value = Long.parseLong(numberString, radix);
return new LongLiteral(numberToken, value);
} catch (NumberFormatException nfe) {
throw new WrappedELException(new SpelException(numberToken.getCharPositionInLine(), nfe,
throw new WrappedSpelException(new SpelException(numberToken.getCharPositionInLine(), nfe,
SpelMessages.NOT_A_LONG, numberToken.getText()));
}
} else {
@@ -92,7 +93,7 @@ public abstract class Literal extends SpelNodeImpl {
int value = Integer.parseInt(numberString, radix);
return new IntLiteral(numberToken, value);
} catch (NumberFormatException nfe) {
throw new WrappedELException(new SpelException(numberToken.getCharPositionInLine(), nfe,
throw new WrappedSpelException(new SpelException(numberToken.getCharPositionInLine(), nfe,
SpelMessages.NOT_AN_INTEGER, numberToken.getText()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,16 @@
* 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;
/**
* Expression language AST node that represents a long integer literal.
*
*
* @author Andy Clement
* @since 3.0
*/
public class LongLiteral extends Literal {
@@ -33,7 +35,7 @@ public class LongLiteral extends Literal {
@Override
public Long getLiteralValue() {
return value;
return this.value;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,11 +13,13 @@
* 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.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
@@ -26,18 +28,25 @@ import org.springframework.expression.MethodResolver;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.internal.Utils;
/**
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class MethodReference extends SpelNodeImpl {
private final String name;
private MethodExecutor fastInvocationAccessor;
private volatile MethodExecutor cachedExecutor;
public MethodReference(Token payload) {
super(payload);
name = payload.getText();
}
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Object currentContext = state.getActiveContextObject();
@@ -50,30 +59,36 @@ public class MethodReference extends SpelNodeImpl {
formatMethodForMessage(name, getTypes(arguments)));
}
if (fastInvocationAccessor != null) {
MethodExecutor executorToUse = this.cachedExecutor;
if (executorToUse != 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
return executorToUse.execute(
state.getEvaluationContext(), state.getActiveContextObject(), 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
this.cachedExecutor = null;
}
}
// either there was no accessor or it no longer existed
fastInvocationAccessor = findAccessorForMethod(name, getTypes(arguments), state);
executorToUse = findAccessorForMethod(this.name, getTypes(arguments), state);
this.cachedExecutor = executorToUse;
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());
return executorToUse.execute(
state.getEvaluationContext(), state.getActiveContextObject(), arguments);
}
catch (AccessException ae) {
throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_METHOD_INVOCATION,
this.name, state.getActiveContextObject().getClass().getName(), ae.getMessage());
}
}
private Class<?>[] getTypes(Object... arguments) {
if (arguments == null)
if (arguments == null) {
return null;
}
Class<?>[] argumentTypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++) {
argumentTypes[i] = arguments[i].getClass();
@@ -120,37 +135,33 @@ public class MethodReference extends SpelNodeImpl {
return false;
}
public final MethodExecutor findAccessorForMethod(String name, Class<?>[] argumentTypes, ExpressionState state)
protected 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));
throw new SpelException(SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT,
FormatHelper.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)
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());
}
}
catch (AccessException ex) {
throw new SpelException(ex, 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()));
throw new SpelException(SpelMessages.METHOD_NOT_FOUND, FormatHelper.formatMethodForMessage(name, argumentTypes),
FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class<?>) contextObject) : contextObject.getClass()));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,10 +13,15 @@
* 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;
/**
* @author Andy Clement
* @since 3.0
*/
public class NullLiteral extends Literal {
public NullLiteral(Token payload) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -22,8 +23,9 @@ 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
* @since 3.0
*/
public abstract class Operator extends SpelNodeImpl {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -22,8 +23,9 @@ import org.springframework.expression.spel.ExpressionState;
/**
* Represents the boolean AND operation.
*
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorAnd extends Operator {
@@ -42,8 +44,9 @@ public class OperatorAnd extends Operator {
boolean rightValue;
try {
leftValue = state.toBoolean(getLeftOperand().getValueInternal(state));
} catch (SpelException ee) {
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class);
}
catch (SpelException ee) {
ee.setPosition(getLeftOperand().getCharPositionInLine());
throw ee;
}
@@ -53,8 +56,9 @@ public class OperatorAnd extends Operator {
}
try {
rightValue = state.toBoolean(getRightOperand().getValueInternal(state));
} catch (SpelException ee) {
rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class);
}
catch (SpelException ee) {
ee.setPosition(getRightOperand().getCharPositionInLine());
throw ee;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.List;
@@ -30,6 +31,7 @@ import org.springframework.expression.spel.SpelMessages;
* in the list. The definition of between being inclusive follows the SQL BETWEEN definition.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorBetween extends Operator {
@@ -45,7 +47,6 @@ public class OperatorBetween extends Operator {
/**
* Returns a boolean based on whether a value is in the range expressed. The first operand is any value whilst the
* second is a list of two values - those two values being the bounds allowed for the first operand (inclusive).
*
* @param state the expression state
* @return true if the left operand is in the range specified, false otherwise
* @throws EvaluationException if there is a problem evaluating the expression

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -21,9 +22,10 @@ import org.springframework.expression.Operation;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements division operator
*
* Implements division operator.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorDivide extends Operator {
@@ -44,17 +46,16 @@ public class OperatorDivide extends Operator {
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 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 { // TODO what about non-int result of the division?
return op1.intValue() / op2.intValue();
}
}
return state.operate(Operation.DIVIDE, operandOne, operandTwo);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -21,8 +22,9 @@ import org.springframework.expression.spel.ExpressionState;
/**
* Implements equality operator.
*
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorEquality extends Operator {
@@ -44,11 +46,14 @@ public class OperatorEquality extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() == op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() == op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() == op2.longValue();
} else {
}
else {
return op1.intValue() == op2.intValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements greater than operator.
*
* Implements greater-than operator.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorGreaterThan extends Operator {
@@ -44,11 +46,14 @@ public class OperatorGreaterThan extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() > op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() > op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() > op2.longValue();
} else {
}
else {
return op1.intValue() > op2.intValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements greater than or equal operator.
*
* Implements greater-than-or-equal operator.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorGreaterThanOrEqual extends Operator {
@@ -44,11 +46,14 @@ public class OperatorGreaterThanOrEqual extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() >= op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() >= op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() >= op2.longValue();
} else {
}
else {
return op1.intValue() >= op2.intValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the not-equal operator
*
* Implements the not-equal operator.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorInequality extends Operator {
@@ -44,11 +46,14 @@ public class OperatorInequality extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() != op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() != op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() != op2.longValue();
} else {
}
else {
return op1.intValue() != op2.intValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,19 +13,22 @@
* 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.SpelException;
import org.springframework.expression.spel.SpelMessages;
/**
* The operator 'instanceof' checks if an object is of the class specified in the right hand operand, in the same way
* that instanceof does in Java.
*
* The operator 'instanceof' checks if an object is of the class specified in the right hand operand,
* in the same way that <code>instanceof</code> does in Java.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorInstanceof extends Operator {
@@ -39,9 +42,8 @@ public class OperatorInstanceof extends Operator {
}
/**
* Compare the left operand to see it is an instance of the type specified as the right operand. The right operand
* must be a class.
*
* Compare the left operand to see it is an instance of the type specified as the right operand.
* The right operand must be a class.
* @param state the expression state
* @return true if the left operand is an instanceof of the right operand, otherwise false
* @throws EvaluationException if there is a problem evaluating the expression
@@ -51,11 +53,12 @@ public class OperatorInstanceof extends Operator {
Object left = getLeftOperand().getValueInternal(state);
Object right = getRightOperand().getValueInternal(state);
if (left == null) {
return false; // null is not an instanceof anything
return false; // null is not an instanceof anything
}
if (right == null || !(right instanceof Class<?>)) {
throw new SpelException(getRightOperand().getCharPositionInLine(),
SpelMessages.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, (right == null ? "null" : right.getClass().getName()));
SpelMessages.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND,
(right == null ? "null" : right.getClass().getName()));
}
Class<?> rightClass = (Class<?>) right;
return rightClass.isAssignableFrom(left.getClass());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the less than operator
*
* Implements the less-than operator.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorLessThan extends Operator {
@@ -44,11 +46,14 @@ public class OperatorLessThan extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() < op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() < op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() < op2.longValue();
} else {
}
else {
return op1.intValue() < op2.intValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
/**
* Implements the less than or equal operator
*
* Implements the less-than-or-equal operator.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorLessThanOrEqual extends Operator {
@@ -39,11 +41,14 @@ public class OperatorLessThanOrEqual extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return op1.doubleValue() <= op2.doubleValue();
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return op1.floatValue() <= op2.floatValue();
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return op1.longValue() <= op2.longValue();
} else {
}
else {
return op1.intValue() <= op2.intValue();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -28,8 +29,9 @@ import org.springframework.expression.spel.SpelMessages;
/**
* Implements the matches operator. Matches takes two operands. The first is a string and the second is a java regex. It
* will return true when getValue() is called if the first operand matches the regex.
*
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorMatches extends Operator {
@@ -44,7 +46,6 @@ public class OperatorMatches extends Operator {
/**
* Check the first operand matches the regex specified as the second operand.
*
* @param state the expression state
* @return true if the first operand matches the regex specified as the second operand, otherwise false
* @throws EvaluationException if there is a problem evaluating the expression (e.g. the regex is invalid)
@@ -67,7 +68,8 @@ public class OperatorMatches extends Operator {
Pattern pattern = Pattern.compile((String) right);
Matcher matcher = pattern.matcher((String) left);
return matcher.matches();
} catch (PatternSyntaxException pse) {
}
catch (PatternSyntaxException pse) {
throw new SpelException(rightOp.getCharPositionInLine(), pse, SpelMessages.INVALID_PATTERN, right);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -24,8 +25,9 @@ import org.springframework.expression.spel.SpelMessages;
/**
* Implements the minus operator. If there is only one operand it is a unary minus.
*
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorMinus extends Operator {
@@ -55,41 +57,41 @@ public class OperatorMinus extends Operator {
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;
return 0 - n.doubleValue();
}
else if (left instanceof Float) {
return 0 - n.floatValue();
}
else if (left instanceof Long) {
return 0 - n.longValue();
}
else {
return 0 - n.intValue();
}
}
throw new SpelException(SpelMessages.CANNOT_NEGATE_TYPE, left.getClass().getName());
} else {
}
else {
Object left = leftOp.getValueInternal(state);
Object right = rightOp.getValueInternal(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 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.operate(Operation.SUBTRACT, left, right);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -22,8 +23,9 @@ import org.springframework.expression.spel.ExpressionState;
/**
* Implements the modulus operator.
*
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorModulus extends Operator {
@@ -44,17 +46,16 @@ public class OperatorModulus extends Operator {
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 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.operate(Operation.MODULUS, operandOne, operandTwo);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -23,14 +24,15 @@ 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 (<28>5.1.8) is performed. Then:<br>
*
* <p>If any of the operands is of a reference type, unboxing conversion (<28>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
* @since 3.0
*/
public class OperatorMultiply extends Operator {
@@ -55,20 +57,20 @@ public class OperatorMultiply extends Operator {
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;
return op1.doubleValue() * op2.doubleValue();
}
} else if (operandOne instanceof String && operandTwo instanceof Integer) {
int repeats = ((Integer) operandTwo).intValue();
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();
}
}
else if (operandOne instanceof String && operandTwo instanceof Integer) {
int repeats = (Integer) operandTwo;
StringBuilder result = new StringBuilder();
for (int i = 0; i < repeats; i++) {
result.append(operandOne);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -20,6 +21,12 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a NOT operation.
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do not extend BinaryOperator
public OperatorNot(Token payload) {
@@ -29,9 +36,10 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
try {
boolean value = state.toBoolean(getChild(0).getValueInternal(state));
boolean value = state.convertValue(getChild(0).getValueInternal(state), Boolean.class);
return !value;
} catch (SpelException see) {
}
catch (SpelException see) {
see.setPosition(getChild(0).getCharPositionInLine());
throw see;
}
@@ -48,4 +56,5 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -22,8 +23,9 @@ import org.springframework.expression.spel.ExpressionState;
/**
* Represents the boolean OR operation.
*
*
* @author Andy Clement
* @since 3.0
*/
public class OperatorOr extends Operator {
@@ -41,18 +43,21 @@ public class OperatorOr extends Operator {
boolean leftValue;
boolean rightValue;
try {
leftValue = state.toBoolean(getLeftOperand().getValueInternal(state));
} catch (SpelException see) {
leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class);
}
catch (SpelException see) {
see.setPosition(getLeftOperand().getCharPositionInLine());
throw see;
}
if (leftValue == true)
if (leftValue == true) {
return true; // no need to evaluate right operand
}
try {
rightValue = state.toBoolean(getRightOperand().getValueInternal(state));
} catch (SpelException see) {
rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class);
}
catch (SpelException see) {
see.setPosition(getRightOperand().getCharPositionInLine());
throw see;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,19 @@
* 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;
/**
* @author Andy Clement
* @since 3.0
*/
public class OperatorPlus extends Operator {
public OperatorPlus(Token payload) {
@@ -33,41 +39,40 @@ public class OperatorPlus extends Operator {
if (rightOp == null) { // If only one operand, then this is unary plus
Object operandOne = leftOp.getValueInternal(state);
if (operandOne instanceof Number) {
return new Integer(((Number) operandOne).intValue());
return ((Number) operandOne).intValue();
}
return state.operate(Operation.ADD, operandOne, null);
} else {
}
else {
Object operandOne = leftOp.getValueInternal(state);
Object operandTwo = rightOp.getValueInternal(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;
return op1.doubleValue() + op2.doubleValue();
}
} else if (operandOne instanceof String && operandTwo instanceof String) {
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 { // TODO what about overflow?
return op1.intValue() + op2.intValue();
}
}
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) {
}
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 Character.toString((char) (l.charAt(0) + i));
}
return new StringBuilder((String) operandOne).append(((Integer) operandTwo).toString()).toString();
return new StringBuilder(l).append(i).toString();
}
return state.operate(Operation.ADD, operandOne, operandTwo);
}
@@ -80,9 +85,10 @@ public class OperatorPlus extends Operator {
@Override
public String toStringAST() {
if (getRightOperand() == null) { // unary plus
if (getRightOperand() == null) { // unary plus
return new StringBuilder().append("+").append(getLeftOperand()).toString();
}
return super.toStringAST();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -25,7 +26,7 @@ import org.springframework.expression.spel.ExpressionState;
* information for messages/etc.
*
* @author Andy Clement
*
* @since 3.0
*/
public class Placeholder extends SpelNodeImpl {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
@@ -21,11 +22,11 @@ import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
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

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,84 +13,87 @@
* 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.ExpressionState;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.internal.Utils;
/**
* Represents a simple property or field reference.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class PropertyOrFieldReference extends SpelNodeImpl {
public static boolean useCaching = true;
private final String name;
private volatile PropertyAccessor cachedReadAccessor;
private volatile PropertyAccessor cachedWriteAccessor;
private final Object name;
private PropertyReaderExecutor cachedReaderExecutor;
private PropertyWriterExecutor cachedWriterExecutor;
public PropertyOrFieldReference(Token payload) {
super(payload);
name = payload.getText();
this.name = payload.getText();
}
@Override
public Object getValueInternal(ExpressionState state) throws SpelException {
return readProperty(state, name);
return readProperty(state, this.name);
}
@Override
public void setValue(ExpressionState state, Object newValue) throws SpelException {
writeProperty(state, name, newValue);
writeProperty(state, this.name, newValue);
}
@Override
public boolean isWritable(ExpressionState state) throws SpelException {
return isWritableProperty(name, state);
return isWritableProperty(this.name, state);
}
@Override
public String toStringAST() {
return name.toString();
return this.name;
}
/**
* 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 {
private Object readProperty(ExpressionState state, String name) throws SpelException {
Object contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
if (cachedReaderExecutor != null) {
PropertyAccessor accessorToUse = this.cachedReadAccessor;
if (accessorToUse != null) {
try {
return cachedReaderExecutor.execute(state.getEvaluationContext(), contextObject);
} catch (AccessException ae) {
return accessorToUse.read(state.getEvaluationContext(), contextObject, name);
}
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
this.cachedReadAccessor = null;
}
}
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
@@ -99,47 +102,34 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
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;
}
if (accessor.canRead(eContext, contextObject, name)) {
this.cachedReadAccessor = accessor;
return accessor.read(eContext, contextObject, name);
}
}
} catch (AccessException ae) {
}
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));
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, name,
FormatHelper.formatClassNameForMessage(contextObjectClass));
}
private void writeProperty(ExpressionState state, Object name, Object newValue) throws SpelException {
private void writeProperty(ExpressionState state, String name, Object newValue) throws SpelException {
Object contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
if (cachedWriterExecutor != null) {
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
if (accessorToUse != null) {
try {
cachedWriterExecutor.execute(state.getEvaluationContext(), contextObject, newValue);
accessorToUse.write(state.getEvaluationContext(), contextObject, name, newValue);
return;
} catch (AccessException ae) {
}
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
this.cachedWriteAccessor = null;
}
}
@@ -149,27 +139,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
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;
}
if (accessor.canWrite(eContext, contextObject, name)) {
this.cachedWriteAccessor = accessor;
accessor.write(eContext, contextObject, name, newValue);
return;
}
}
} catch (AccessException ae) {
@@ -177,11 +150,11 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
name, ae.getMessage());
}
}
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND, name, Utils
.formatClassnameForMessage(contextObjectClass));
throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND, name, FormatHelper
.formatClassNameForMessage(contextObjectClass));
}
public boolean isWritableProperty(Object name, ExpressionState state) throws SpelException {
public boolean isWritableProperty(String name, ExpressionState state) throws SpelException {
Object contextObject = state.getActiveContextObject();
EvaluationContext eContext = state.getEvaluationContext();
if (contextObject == null) {
@@ -210,7 +183,6 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
* 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
*/
@@ -221,15 +193,16 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
Class<?>[] targets = resolver.getSpecificTargetClasses();
if (targets == null) { // generic resolver that says it can be used for any type
generalAccessors.add(resolver);
} else {
}
else {
if (targetType != null) {
int pos = 0;
for (int i = 0; i < targets.length; i++) {
Class<?> clazz = targets[i];
for (Class<?> clazz : targets) {
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
}
else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the
// specificAccessor list
generalAccessors.add(resolver);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,19 +13,21 @@
* 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')"
*
*
* <p>Example: "java.lang.String" as in the expression "new java.lang.String('hello')"
*
* @author Andy Clement
*
* @since 3.0
*/
public class QualifiedIdentifier extends SpelNodeImpl {
@@ -39,27 +41,30 @@ public class QualifiedIdentifier extends SpelNodeImpl {
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
// Cache the concatenation of child identifiers
if (value == null) {
if (this.value == null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
if (i > 0) {
sb.append(".");
}
sb.append(getChild(i).getValueInternal(state));
}
value = sb.toString();
this.value = sb.toString();
}
return value;
return this.value;
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
if (value != null) {
sb.append(value);
} else {
if (this.value != null) {
sb.append(this.value);
}
else {
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
if (i > 0) {
sb.append(".");
}
sb.append(getChild(i).toStringAST());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,10 +13,15 @@
* 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;
/**
* @author Andy Clement
* @since 3.0
*/
public class RealLiteral extends Literal {
private final Double value;
@@ -28,7 +33,7 @@ public class RealLiteral extends Literal {
@Override
public Double getLiteralValue() {
return value;
return this.value;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
@@ -21,11 +22,11 @@ import java.util.List;
import java.util.Map;
import org.antlr.runtime.Token;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.ExpressionState;
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

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,32 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import java.io.Serializable;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.common.ExpressionUtils;
import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.SpelException;
import org.springframework.expression.spel.SpelMessages;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.generated.SpringExpressionsParser;
import org.springframework.expression.spel.standard.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
/**
* The common supertype of all AST nodes in a parsed Spring Expression Language format expression.
*
*
* @author Andy Clement
*
* @since 3.0
*/
public abstract class SpelNodeImpl extends CommonTree implements Serializable, SpelNode {
public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Serializable {
/**
* The Antlr parser uses this constructor to build SpelNodes.
*
* @param payload the token for the node that has been parsed
*/
protected SpelNodeImpl(Token payload) {
@@ -46,38 +47,23 @@ public abstract class SpelNodeImpl extends CommonTree implements Serializable, S
}
public final Object getValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState==null) {
return getValue(new ExpressionState(new StandardEvaluationContext()));
} else {
if (expressionState != null) {
return getValueInternal(expressionState);
}
else {
return getValue(new ExpressionState(new StandardEvaluationContext()));
}
}
/* (non-Javadoc)
* @see org.springframework.expression.spel.ast.ISpelNode#getValue(org.springframework.expression.spel.ExpressionState)
*/
public abstract Object getValueInternal(ExpressionState expressionState) throws EvaluationException;
/* (non-Javadoc)
* @see org.springframework.expression.spel.ast.ISpelNode#isWritable(org.springframework.expression.spel.ExpressionState)
*/
public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
return false;
}
/* (non-Javadoc)
* @see org.springframework.expression.spel.ast.ISpelNode#setValue(org.springframework.expression.spel.ExpressionState, java.lang.Object)
*/
public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
throw new SpelException(getCharPositionInLine(), SpelMessages.SETVALUE_NOT_SUPPORTED, getClass(),
getTokenName());
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";
@@ -85,42 +71,39 @@ public abstract class SpelNodeImpl extends CommonTree implements Serializable, S
return SpringExpressionsParser.tokenNames[getToken().getType()];
}
/* (non-Javadoc)
* @see org.springframework.expression.spel.ast.ISpelNode#toStringAST()
*/
public abstract String toStringAST();
/* (non-Javadoc)
* @see org.springframework.expression.spel.ast.ISpelNode#getChild(int)
*/
@Override
public SpelNodeImpl getChild(int index) {
return (SpelNodeImpl) super.getChild(index);
}
/* (non-Javadoc)
* @see org.springframework.expression.spel.ast.ISpelNode#getObjectClass(java.lang.Object)
*/
public Class<?> getObjectClass(Object o) {
if (o == null)
public Class<?> getObjectClass(Object obj) {
if (obj == null) {
return null;
return (o instanceof Class) ? ((Class<?>) o) : o.getClass();
}
return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
}
protected final Object getValue(ExpressionState state, Class<?> desiredReturnType) throws EvaluationException {
@SuppressWarnings("unchecked")
protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
Object result = getValueInternal(state);
if (result != null && desiredReturnType != null) {
Class<?> resultType = result.getClass();
if (desiredReturnType.isAssignableFrom(resultType)) {
return result;
return (T) result;
}
// Attempt conversion to the requested type, may throw an exception
return ExpressionUtils.convert(state.getEvaluationContext(), result, desiredReturnType);
}
return result;
return (T) result;
}
public int getStartPosition() {
return getCharPositionInLine();
}
public abstract Object getValueInternal(ExpressionState expressionState) throws EvaluationException;
public abstract String toStringAST();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.ast;
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTreeAdaptor;
import org.springframework.expression.spel.generated.SpringExpressionsLexer;
/**
* @author Andy Clement
* @since 3.0
*/
public class SpelTreeAdaptor extends CommonTreeAdaptor {
@Override
public Object create(Token payload) {
if (payload != null) {
@@ -131,4 +138,5 @@ public class SpelTreeAdaptor extends CommonTreeAdaptor {
}
return new EmptySpelNode(payload);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,30 +13,36 @@
* 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;
/**
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class StringLiteral extends Literal {
private String value;
private final String value;
public StringLiteral(Token payload) {
super(payload);
value = payload.getText();
String val = 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("''", "'");
val = val.substring(1, val.length() - 1);
this.value = val.replaceAll("''", "'");
}
@Override
public String getLiteralValue() {
return value;
return this.value;
}
@Override
public String toString() {
return new StringBuilder("'").append(getLiteralValue()).append("'").toString();
return "'" + getLiteralValue() + "'";
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* 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;
@@ -22,8 +23,10 @@ import org.springframework.expression.spel.SpelException;
/**
* Represents a ternary expression, for example: "someCheck()?true:false".
*
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class Ternary extends SpelNodeImpl {
@@ -33,21 +36,22 @@ public class Ternary extends SpelNodeImpl {
/**
* Evaluate the condition and if true evaluate the first alternative, otherwise evaluate the second alternative.
*
* @param state the expression state
* @throws EvaluationException if the condition does not evaluate correctly to a boolean or there is a problem
* executing the chosen alternative
*/
@Override
public Object getValueInternal(ExpressionState state) throws EvaluationException {
Boolean b = (Boolean) getChild(0).getValue(state, Boolean.class);
Boolean b = getChild(0).getValue(state, Boolean.class);
try {
if (b) {
if (b != null && b.booleanValue()) {
return getChild(1).getValueInternal(state);
} else {
}
else {
return getChild(2).getValueInternal(state);
}
} catch (SpelException ex) {
}
catch (SpelException ex) {
ex.setPosition(getChild(0).getCharPositionInLine());
throw ex;
}
@@ -63,4 +67,5 @@ public class Ternary extends SpelNodeImpl {
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.internal;
public enum TypeCode {
package org.springframework.expression.spel.ast;
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);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,19 +13,19 @@
* 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.SpelException;
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 SpelNodeImpl {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2008 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,58 +13,64 @@
* 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.ExpressionState;
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
*
* @since 3.0
*/
public class VariableReference extends SpelNodeImpl {
// Well known variables:
private final static String THIS = "this"; // currently active context object
private final static String ROOT = "root"; // root context object
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();
this.name = payload.getText();
}
@Override
public Object getValueInternal(ExpressionState state) throws SpelException {
if (name.equals(THIS))
if (this.name.equals(THIS)) {
return state.getActiveContextObject();
if (name.equals(ROOT))
}
if (this.name.equals(ROOT)) {
return state.getRootContextObject();
Object result = state.lookupVariable(name);
}
Object result = state.lookupVariable(this.name);
if (result == null) {
throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, name);
throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, this.name);
}
return result;
}
@Override
public void setValue(ExpressionState state, Object value) throws SpelException {
// Object oldValue = state.lookupVariable(name);
state.setVariable(name, value);
state.setVariable(this.name, value);
}
@Override
public String toStringAST() {
return new StringBuilder("#").append(name).toString();
return "#" + this.name;
}
@Override
public boolean isWritable(ExpressionState expressionState) throws SpelException {
return !(name.equals(THIS) || name.equals(ROOT));
return !(this.name.equals(THIS) || this.name.equals(ROOT));
}
}

View File

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

View File

@@ -1,74 +0,0 @@
/*
* 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;
import org.springframework.expression.EvaluationException;
/**
* 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) {
c = constructor;
this.argsRequiringConversion = argsRequiringConversion;
}
/**
* Invoke a constructor via reflection.
*/
public Object execute(EvaluationContext context, Object... arguments) throws AccessException {
if (argsRequiringConversion != null && arguments != null) {
try {
ReflectionUtils.convertArguments(c.getParameterTypes(), c.isVarArgs(), context.getTypeUtils()
.getTypeConverter(), argsRequiringConversion, arguments);
} catch (EvaluationException ex) {
throw new AccessException("Problem invoking constructor on '" + c + "': " + ex.getMessage(), ex);
}
}
if (c.isVarArgs()) {
arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(c.getParameterTypes(), arguments);
}
try {
if (!c.isAccessible()) {
c.setAccessible(true);
}
return c.newInstance(arguments);
} catch (IllegalArgumentException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
} catch (InstantiationException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
}
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.reflection;
import org.springframework.expression.AccessException;
import org.springframework.expression.ConstructorExecutor;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredConstructor;
/**
* A constructor resolver that uses reflection to locate the constructor that should be invoked
*
* @author Andy Clement
*/
public class ReflectionConstructorResolver implements ConstructorResolver {
/*
* Indicates if this resolve will allow matches to be found that require some of the input arguments to be
* transformed by the conversion service.
*/
private boolean allowMatchesRequiringArgumentConversion = true;
public ReflectionConstructorResolver() {
}
public ReflectionConstructorResolver(boolean allowMatchesRequiringArgumentConversion) {
this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion;
}
public void setAllowMatchRequiringArgumentConversion(boolean allow) {
this.allowMatchesRequiringArgumentConversion = allow;
}
/**
* Locate a matching constructor or return null if non can be found.
*/
public ConstructorExecutor resolve(EvaluationContext context, String typename, Class<?>[] argumentTypes)
throws AccessException {
try {
Class<?> c = context.getTypeUtils().getTypeLocator().findType(typename);
DiscoveredConstructor dCtor = ReflectionUtils.findConstructor(context.getTypeUtils().getTypeConverter(), c,
argumentTypes, allowMatchesRequiringArgumentConversion);
if (dCtor == null) {
return null;
}
return new ReflectionConstructorExecutor(dCtor.theConstructor, dCtor.argumentsRequiringConversion);
} catch (EvaluationException e) {
throw new AccessException(null,e);
}
}
}

View File

@@ -1,70 +0,0 @@
/*
* 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.EvaluationException;
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) {
m = theMethod;
argsRequiringConversion = argumentsRequiringConversion;
}
public Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
if (argsRequiringConversion != null && arguments != null) {
try {
ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), context.getTypeUtils()
.getTypeConverter(), argsRequiringConversion, arguments);
} catch (EvaluationException ex) {
throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass()
+ "': " + ex.getMessage(), ex);
}
}
if (m.isVarArgs()) {
arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), arguments);
}
try {
if (!m.isAccessible()) {
m.setAccessible(true);
}
return m.invoke(target, arguments);
} catch (IllegalArgumentException e) {
throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+ e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+ e.getMessage(), e);
} catch (InvocationTargetException e) {
e.getCause().printStackTrace();
throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+ e.getMessage(), e);
}
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2004-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.reflection;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredMethod;
/**
* A method resolver that uses reflection to locate the method that should be invoked
*
* @author Andy Clement
*/
public class ReflectionMethodResolver implements MethodResolver {
/*
* Indicates if this resolve will allow matches to be found that require some of the input arguments to be
* transformed by the conversion service.
*/
private boolean allowMatchesRequiringArgumentConversion = true;
public ReflectionMethodResolver() {
}
public ReflectionMethodResolver(boolean allowMatchesRequiringArgumentConversion) {
this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion;
}
public void setAllowMatchRequiringArgumentConversion(boolean allow) {
this.allowMatchesRequiringArgumentConversion = allow;
}
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class<?>[] argumentTypes) throws AccessException {
try {
Class<?> relevantClass = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
DiscoveredMethod dMethod = ReflectionUtils.findMethod(context.getTypeUtils().getTypeConverter(), name,
argumentTypes, relevantClass, allowMatchesRequiringArgumentConversion);
if (dMethod == null) {
return null;
}
return new ReflectionMethodExecutor(dMethod.theMethod, dMethod.argumentsRequiringConversion);
} catch (EvaluationException e) {
throw new AccessException(null,e);
}
}
}

View File

@@ -1,57 +0,0 @@
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 final String propertyName;
public ReflectionPropertyReaderExecutor(String propertyName, Method method) {
this.propertyName = propertyName;
methodToAccessProperty = method;
}
public ReflectionPropertyReaderExecutor(String propertyName, Field field) {
this.propertyName = propertyName;
fieldToAccessProperty = field;
}
public Object execute(EvaluationContext context, Object target) throws AccessException {
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);
}
}
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);
}
}
throw new AccessException("No method or field accessor found for property '" + propertyName + "'");
}
}

View File

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

View File

@@ -1,223 +0,0 @@
/*
* 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). This
* implementation currently follows the Resolver/Executor model (it extends CacheablePropertyAccessor) - the code that
* would be used if it were a simple property accessor is shown at the end.
*
* @author Andy Clement
*/
public class ReflectionPropertyResolver extends CacheablePropertyAccessor {
/**
* @return null which means this is a general purpose accessor
*/
public Class<?>[] getSpecificTargetClasses() {
return null;
}
/**
* Use reflection to discover if a named property is accessible on an target type and if it is return an executor
* object that can be called repeatedly to retrieve that property. A property is accessible either as a field or
* through a getter.
*
* @param context the context in which the access is being attempted
* @param target the target object on which the property is being accessed
* @param name the name of the property
*/
@Override
public PropertyReaderExecutor getReaderAccessor(EvaluationContext context, Object target, Object name) {
if (target == null) {
return null;
}
Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
if (!(name instanceof String)) {
// A property not found exception will occur if the reflection finder was supposed to find it
return null;
}
String propertyName = (String) name;
if (relevantClass.isArray() && propertyName.equals("length")) {
return new ReflectionPropertyReaderExecutorForArrayLength();
}
Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass, target instanceof Class);
if (m != null) {
return new ReflectionPropertyReaderExecutor(propertyName, m);
}
Field field = ReflectionUtils.findField(propertyName, relevantClass, target instanceof Class);
if (field != null) {
return new ReflectionPropertyReaderExecutor(propertyName, field);
}
return null;
}
/**
* Use reflection to discover if a named property is accessible on an target type and if it is return an executor
* object that can be called repeatedly to set that property. A property is writable either as a field or through a
* setter.
*
* @param context the context in which the set is being attempted
* @param target the target object on which the property is being set
* @param name the name of the property
*/
@Override
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)) {
// A property not found exception will occur if the reflection finder was supposed to find it
return null;
}
Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass, target instanceof Class);
if (m != null) {
return new ReflectionPropertyWriterExecutor((String) name, m);
}
Field field = ReflectionUtils.findField((String) name, relevantClass, target instanceof Class);
if (field != null) {
return new ReflectionPropertyWriterExecutor((String) name, field);
}
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;
// }
// 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;
// }
// 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 boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException {
// if (target==null) {
// return false;
// }
// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
// if (!(name instanceof String))
// return false;
// Field field = ReflectionUtils.findField((String) name, relevantClass);
// if (field != null)
// return true;
// Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass);
// if (m != null)
// return true;
// return false;
// }
}

View File

@@ -1,58 +0,0 @@
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 final String propertyName;
public ReflectionPropertyWriterExecutor(String propertyName, Method method) {
this.propertyName = propertyName;
methodToAccessProperty = method;
}
public ReflectionPropertyWriterExecutor(String propertyName, Field field) {
this.propertyName = propertyName;
fieldToAccessProperty = field;
}
// public Object execute(EvaluationContext context, Object target) throws AccessException {
public void execute(EvaluationContext evaluationContext, Object target, Object newValue) throws AccessException {
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);
}
}
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);
}
}
throw new AccessException("No method or field accessor found for property '" + propertyName + "'");
}
}

View File

@@ -1,562 +0,0 @@
/*
* 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.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
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) {
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
*/
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
*/
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);
}
}
}
// TODO optimize implementation of areBoxingCompatible
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, boolean mustBeStatic) {
Field[] fields = clazz.getFields(); // TODO use getDeclaredFields() and search up hierarchy?
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.getName().equals(name) && (mustBeStatic ? Modifier.isStatic(field.getModifiers()) : true)) {
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, boolean mustBeStatic) {
Method[] ms = clazz.getMethods();// TODO use getDeclaredMethods() and search up hierarchy?
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
&& (mustBeStatic ? Modifier.isStatic(method.getModifiers()) : true)
&& method.getName().equals(expectedGetterName)) {
return method;
}
}
return null;
}
/**
* Find a setter method for the specified property
*/
public static Method findSetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
Method[] ms = clazz.getMethods(); // TODO use getDeclaredMethods() and search up hierarchy?
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
&& (mustBeStatic ? Modifier.isStatic(method.getModifiers()) : true)
&& 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;
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;
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;
argumentsRequiringConversion = argsToConvert;
}
}
static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
Integer[] argsRequiringConversion, Object... arguments) throws EvaluationException {
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 SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "Converter failed to convert '"
// + arguments[argPosition] + " to type '" + targetType + "'");
// }
}
}
public static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
Object... arguments) throws EvaluationException {
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];
}
if (converter == null) {
throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION,
"No converter available to convert '" + arguments[i] + " to type '" + targetType + "'");
}
try {
if (arguments[i] != null && arguments[i].getClass() != targetType) {
arguments[i] = converter.convertValue(arguments[i], targetType);
}
} catch (EvaluationException e) {
// allows for another type converter throwing a different kind of EvaluationException
if (!(e instanceof SpelException)) {
throw new SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION,
"Converter failed to convert '" + arguments[i].getClass().getName() + "' to type '"
+ targetType + "'");
}
throw 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
*/
public 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 EvaluationException {
if (arguments != null) {
ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, arguments);
}
if (m.isVarArgs()) {
arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), arguments);
}
return arguments;
}
}

View File

@@ -1,203 +0,0 @@
/*
* 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 void reset() {
typeUtils = new StandardTypeUtilities();
methodResolvers.clear();
addMethodResolver(new ReflectionMethodResolver());
constructorResolvers.clear();
addConstructorResolver(new ReflectionConstructorResolver());
propertyResolvers.clear();
addPropertyAccessor(new ReflectionPropertyResolver());
simpleReferencesMap.clear();
variables.clear();
rootObject = null;
}
public StandardEvaluationContext(Object rootContextObject) {
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).toURI().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) {
rootObject = o;
}
// TODO have a variant that adds at position (same for ctor/propOrField)
public void addMethodResolver(MethodResolver resolver) {
methodResolvers.add(resolver);
}
public void removeMethodResolver(MethodResolver resolver) {
methodResolvers.remove(resolver);
}
public void insertMethodResolver(int pos, MethodResolver resolver) {
methodResolvers.add(pos, resolver);
}
public void addConstructorResolver(ConstructorResolver resolver) {
constructorResolvers.add(resolver);
}
public void addReference(String contextName, String objectName, Object value) {
Map<String, Object> contextMap = simpleReferencesMap.get(contextName);
if (contextMap == null) {
contextMap = new HashMap<String, Object>();
simpleReferencesMap.put(contextName, contextMap);
}
contextMap.put(objectName, value);
}
public void addTypeConverter(StandardIndividualTypeConverter newConverter) {
((StandardTypeConverter) typeUtils.getTypeConverter()).registerConverter(newConverter);
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright 2004-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.standard;
import org.springframework.expression.EvaluationException;
/**
* Implementations of this interface are able to convert from some set of types to another type. For
* example they might be able to convert some set of number types (Integer.class, Double.class) to
* a string (String.class). Once created they are registered with the {@link StandardEvaluationContext} or
* {@link StandardTypeConverter}.
*
* @author Andy Clement
*/
public interface StandardIndividualTypeConverter {
/**
* @return return the set of classes which this converter can convert from.
*/
Class<?>[] getFrom();
/**
* @return the class which this converter can convert to.
*/
Class<?> getTo();
/**
* Return a value converted to the type that {@link #getTo()} specified.
*
* @param value the object to convert
* @return the converted value
* @throws EvaluationException if there is a problem during conversion
*/
Object convert(Object value) throws EvaluationException;
}

View File

@@ -1,352 +0,0 @@
/*
* 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) {
if (sourceType == Integer.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Integer.class);
} else if (targetType == Boolean.TYPE) {
if (sourceType == Boolean.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Boolean.class);
} else if (targetType == Short.TYPE) {
if (sourceType == Short.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Short.class);
} else if (targetType == Long.TYPE) {
if (sourceType == Long.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Long.class);
} else if (targetType == Character.TYPE) {
if (sourceType == Character.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Character.class);
} else if (targetType == Double.TYPE) {
if (sourceType == Double.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Double.class);
} else if (targetType == Float.TYPE) {
if (sourceType == Float.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Float.class);
} else if (targetType == Byte.TYPE) {
if (sourceType == Byte.class) {
return true;
}
possibleConvertersToTheTargetType = converters.get(Byte.class);
}
}
if (possibleConvertersToTheTargetType != null) {
StandardIndividualTypeConverter aConverter = possibleConvertersToTheTargetType.get(sourceType);
if (aConverter != null) {
return true;
}
}
return false;
}
// TODO 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;
Class sourceType = value.getClass();
Map<Class<?>, StandardIndividualTypeConverter> possibleConvertersToTheTargetType = converters.get(targetType);
if (possibleConvertersToTheTargetType == null && targetType.isPrimitive()) {
if (targetType == Integer.TYPE) {
if (sourceType == Integer.class) {
return value;
}
possibleConvertersToTheTargetType = converters.get(Integer.class);
} else if (targetType == Boolean.TYPE) {
if (sourceType == Boolean.class) {
return value;
}
possibleConvertersToTheTargetType = converters.get(Boolean.class);
} else if (targetType == Short.TYPE) {
if (sourceType == Short.class) {
return value;
}
possibleConvertersToTheTargetType = converters.get(Short.class);
} else if (targetType == Long.TYPE) {
if (sourceType == Long.class) {
return value;
}
possibleConvertersToTheTargetType = converters.get(Long.class);
} else if (targetType == Character.TYPE) {
if (sourceType == Character.class) {
return value;
}
possibleConvertersToTheTargetType = converters.get(Character.class);
} else if (targetType == Double.TYPE) {
if (sourceType == Double.class) {
return value;
}
possibleConvertersToTheTargetType = converters.get(Double.class);
} else if (targetType == Float.TYPE) {
if (sourceType == Float.class) {
return value;
}
possibleConvertersToTheTargetType = converters.get(Float.class);
} else if (targetType == Byte.TYPE) {
if (sourceType == Byte.class) {
return value;
}
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 {
if (value instanceof Integer) {
return ((Integer) value).intValue() != 0;
} else {
return ((Long) value).longValue() != 0;
}
}
public Class<?>[] getFrom() {
return new Class<?>[] { Integer.class, Long.class };
}
public Class<?> getTo() {
return Boolean.class;
}
}
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) {
// returning null will mean the caller throws a type conversion related 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 {
if (value instanceof Integer) {
return ((Integer)value).floatValue();
} else {
return ((Double) value).floatValue();
}
}
public Class<?>[] getFrom() {
return new Class<?>[] { Double.class, Integer.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 String) {
try {
return Integer.parseInt((String)value);
} catch (NumberFormatException nfe) {
throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "cannot parse string '" + value
+ "' as an integer");
}
} else { // Long
try {
return Integer.parseInt(((Long) value).toString());
} catch (NumberFormatException nfe) {
throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "long value '" + value
+ "' cannot be represented as an int");
}
}
}
public Class<?>[] getFrom() {
return new Class<?>[] { Long.class, String.class };
}
public Class<?> getTo() {
return Integer.class;
}
}
}

View File

@@ -1,102 +0,0 @@
/*
* 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.OperatorOverloader;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.TypeUtils;
/**
* The StandardTypeUtilities implementation pulls together the standard implementations of the TypeComparator,
* TypeLocator and TypeConverter interfaces. Each of these can be replaced so if only wishing to replace one of those
* type facilities.
*
* @author Andy Clement
*
*/
public class StandardTypeUtilities implements TypeUtils {
private TypeComparator typeComparator;
private TypeLocator typeLocator;
private TypeConverter typeConverter;
private OperatorOverloader operatorOverloader;
public StandardTypeUtilities() {
typeComparator = new StandardComparator();
typeLocator = new StandardTypeLocator();
typeConverter = new StandardTypeConverter();
operatorOverloader = null; // this means operations between basic types are supported (eg. numbers)
}
public TypeLocator getTypeLocator() {
return typeLocator;
}
/**
* Set the type locator for the StandardTypeUtilities object, allows a user to replace parts of the standard
* TypeUtilities implementation if they wish.
*
* @param typeLocator the TypeLocator to use from now on
*/
public void setTypeLocator(TypeLocator typeLocator) {
this.typeLocator = typeLocator;
}
public TypeConverter getTypeConverter() {
return typeConverter;
}
/**
* Set the type converter for the StandardTypeUtilities object, allows a user to replace parts of the standard
* TypeUtilities implementation if they wish.
*
* @param typeConverter the TypeConverter to use from now on
*/
public void setTypeConverter(TypeConverter typeConverter) {
this.typeConverter = typeConverter;
}
public TypeComparator getTypeComparator() {
return typeComparator;
}
/**
* Set the type comparator for the StandardTypeUtilities object, allows a user to replace parts of the standard
* TypeUtilities implementation if they wish.
*
* @param typeComparator the TypeComparator to use from now on
*/
public void setTypeComparator(TypeComparator typeComparator) {
this.typeComparator = typeComparator;
}
public OperatorOverloader getOperatorOverloader() {
return operatorOverloader;
}
/**
* Set the operator overloader for the StandardTypeUtilities object, allows a user to overload the mathematical
* operators to support them between non-standard types.
*
* @param operatorOverloader the OperatorOverloader to use from now on
*/
public void setOperatorOverloader(OperatorOverloader operatorOverloader) {
this.operatorOverloader = operatorOverloader;
}
}

View File

@@ -0,0 +1,315 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.expression.spel.support;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
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.util.ClassUtils;
/**
* Utility methods used by the reflection resolver code to discover the appropriae
* methods/constructors and fields that should be used in expressions.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class ReflectionHelper {
/**
* 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
*/
static ArgumentsMatchInfo compareArguments(
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) {
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 (ClassUtils.isAssignable(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) {
int[] argsArray = new int[argsRequiringConversion.size()];
for (int i = 0; i < argsRequiringConversion.size(); i++) {
argsArray[i] = argsRequiringConversion.get(i);
}
return new ArgumentsMatchInfo(match, argsArray);
}
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
*/
static ArgumentsMatchInfo compareArgumentsVarargs(
Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) {
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) || ClassUtils.isAssignableValue(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 (ClassUtils.isAssignable(varargsParameterType, 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) {
int[] argsArray = new int[argsRequiringConversion.size()];
for (int i = 0; i < argsRequiringConversion.size(); i++) {
argsArray[i] = argsRequiringConversion.get(i);
}
return new ArgumentsMatchInfo(match, argsArray);
}
else {
return new ArgumentsMatchInfo(match);
}
}
}
static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
int[] argsRequiringConversion, Object... arguments) throws EvaluationException {
Class varargsType = null;
if (isVarargs) {
varargsType = parameterTypes[parameterTypes.length - 1].getComponentType();
}
for (Integer argPosition : argsRequiringConversion) {
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 SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "Converter failed to convert '"
// + arguments[argPosition] + " to type '" + targetType + "'");
// }
}
}
public static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
Object... arguments) throws EvaluationException {
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];
}
if (converter == null) {
throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION,
"No converter available to convert '" + arguments[i] + " to type '" + targetType + "'");
}
try {
if (arguments[i] != null && arguments[i].getClass() != targetType) {
arguments[i] = converter.convertValue(arguments[i], targetType);
}
}
catch (EvaluationException ex) {
// allows for another type converter throwing a different kind of EvaluationException
if (ex instanceof SpelException) {
throw ex;
}
else {
throw new SpelException(ex, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION,
"Converter failed to convert '" + arguments[i].getClass().getName() + "' to type '" + targetType + "'");
}
}
}
}
/**
* 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 paramTypes the types of the parameters for the invocation
* @param args the arguments to be setup ready for the invocation
* @return a repackaged array of arguments where any varargs setup has been done
*/
public static Object[] setupArgumentsForVarargsInvocation(Class[] paramTypes, Object... args) {
// Check if array already built for final argument
int nParams = paramTypes.length;
int nArgs = args.length;
// Check if repackaging is needed:
if (nParams != args.length || paramTypes[nParams - 1] != (args[nArgs - 1] == null ? null : args[nArgs - 1].getClass())) {
int arraySize = 0; // zero size array if nothing to pass as the varargs parameter
if (nArgs >= nParams) {
arraySize = nArgs - (nParams - 1);
}
Object[] repackagedArguments = (Object[]) Array.newInstance(paramTypes[nParams - 1].getComponentType(),
arraySize);
// Copy all but the varargs arguments
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = args[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] = args[i];
}
newArgs[newArgs.length - 1] = repackagedArguments;
return newArgs;
}
return args;
}
static enum ArgsMatchKind {
EXACT, CLOSE, REQUIRES_CONVERSION
}
/**
* An instance of ArgumentsMatchInfo 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.
*/
static class ArgumentsMatchInfo {
public ArgsMatchKind kind;
public int[] argsRequiringConversion;
ArgumentsMatchInfo(ArgsMatchKind kind, int[] integers) {
this.kind = kind;
argsRequiringConversion = integers;
}
ArgumentsMatchInfo(ArgsMatchKind kind) {
this.kind = kind;
}
}
}

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