Polish SpEL code

This commit is contained in:
Phillip Webb 2013-06-24 10:52:16 -07:00
parent 4830ea6065
commit e83bdda7d5
93 changed files with 2402 additions and 1658 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,13 +19,15 @@ package org.springframework.expression;
// TODO Is the resolver/executor model too pervasive in this package?
/**
* Executors are built by resolvers and can be cached by the infrastructure to repeat an operation quickly without going
* back to the resolvers. For example, the particular constructor to run on a class may be discovered by the reflection
* constructor resolver - it will then build a ConstructorExecutor that executes that constructor and the
* ConstructorExecutor can be reused without needing to go back to the resolver to discover the constructor again.
* Executors are built by resolvers and can be cached by the infrastructure to repeat an
* operation quickly without going back to the resolvers. For example, the particular
* constructor to run on a class may be discovered by the reflection constructor resolver
* - it will then build a ConstructorExecutor that executes that constructor and the
* ConstructorExecutor can be reused without needing to go back to the resolver to
* discover the constructor again.
*
* 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.
* <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
@ -34,11 +36,13 @@ 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
* @param arguments the arguments to the constructor call, should match (in terms of
* number and type) whatever the command will need to run
* @return the new object
* @throws AccessException if there is a problem executing the command or the CommandExecutor is no longer valid
* @throws AccessException if there is a problem executing the command or the
* CommandExecutor is no longer valid
*/
TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,18 +21,19 @@ import java.util.List;
import org.springframework.core.convert.TypeDescriptor;
/**
* A constructor resolver attempts locate a constructor and returns a ConstructorExecutor that can be used to invoke
* that constructor. The ConstructorExecutor will be cached but if it 'goes stale' the resolvers will be called again.
*
* A constructor resolver attempts locate a constructor and returns a ConstructorExecutor
* that can be used to invoke 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 {
/**
* 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 {@code null} if no constructor could be found).
* 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 {@code 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 argumentTypes the arguments that the constructor must be able to handle

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,12 +19,12 @@ package org.springframework.expression;
import java.util.List;
/**
* 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.
*
* <p>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.
* {@link org.springframework.expression.spel.support.StandardEvaluationContext} that can
* be extended, rather than having to implement everything.
*
* @author Andy Clement
* @author Juergen Hoeller
@ -33,8 +33,9 @@ import java.util.List;
public interface EvaluationContext {
/**
* @return the default root context object against which unqualified properties/methods/etc
* should be resolved. This can be overridden when evaluating an expression.
* @return the default root context object against which unqualified
* properties/methods/etc should be resolved. This can be overridden when
* evaluating an expression.
*/
TypedValue getRootObject();
@ -54,7 +55,8 @@ public interface EvaluationContext {
List<PropertyAccessor> getPropertyAccessors();
/**
* @return a type locator that can be used to find types, either by short or fully qualified name.
* @return a type locator that can be used to find types, either by short or fully
* qualified name.
*/
TypeLocator getTypeLocator();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,10 +19,9 @@ package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
* An expression capable of evaluating itself against context objects.
* Encapsulates the details of a previously parsed expression string.
* Provides a common abstraction for expression evaluation independent
* of any language like OGNL or the Unified EL.
* An expression capable of evaluating itself against context objects. Encapsulates the
* details of a previously parsed expression string. Provides a common abstraction for
* expression evaluation independent of any language like OGNL or the Unified EL.
*
* @author Keith Donald
* @author Andy Clement

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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,7 +16,6 @@
package org.springframework.expression;
/**
* Super class for exceptions that can occur whilst processing expressions
*
@ -27,8 +26,10 @@ package org.springframework.expression;
public class ExpressionException extends RuntimeException {
protected String expressionString;
protected int position; // -1 if not known - but should be known in all reasonable cases
/**
* Creates a new expression exception.
* @param expressionString the expression string
@ -85,15 +86,16 @@ public class ExpressionException extends RuntimeException {
super(message,cause);
}
public String toDetailedString() {
StringBuilder output = new StringBuilder();
if (expressionString!=null) {
if (this.expressionString!=null) {
output.append("Expression '");
output.append(expressionString);
output.append(this.expressionString);
output.append("'");
if (position!=-1) {
if (this.position!=-1) {
output.append(" @ ");
output.append(position);
output.append(this.position);
}
output.append(": ");
}
@ -106,7 +108,7 @@ public class ExpressionException extends RuntimeException {
}
public final int getPosition() {
return position;
return this.position;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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;
/**
* This exception wraps (as cause) a checked exception thrown by some method that SpEL invokes.
* It differs from a SpelEvaluationException because this indicates the occurrence of a checked exception
* that the invoked method was defined to throw. SpelEvaluationExceptions are for handling (and wrapping)
* unexpected exceptions.
* This exception wraps (as cause) a checked exception thrown by some method that SpEL
* invokes. It differs from a SpelEvaluationException because this indicates the
* occurrence of a checked exception that the invoked method was defined to throw.
* SpelEvaluationExceptions are for handling (and wrapping) unexpected exceptions.
*
* @author Andy Clement
* @since 3.0.3

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,13 +17,15 @@
package org.springframework.expression;
/**
* MethodExecutors are built by the resolvers and can be cached by the infrastructure to repeat an operation quickly
* 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.
* MethodExecutors are built by the resolvers and can be cached by the infrastructure to
* repeat an operation quickly 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
* back to the resolvers to ask for a new one.
* <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
@ -31,13 +33,15 @@ package org.springframework.expression;
public interface MethodExecutor {
/**
* Execute a command using the specified arguments, and using the specified expression state.
* 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 arguments the arguments to the executor, should match (in terms of number and type) whatever the
* command will need to run
* @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
* @throws AccessException if there is a problem executing the command or the
* MethodExecutor is no longer valid
*/
TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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;
import java.lang.reflect.Method;
import java.util.List;
/**
* MethodFilter instances allow SpEL users to fine tune the behaviour of the method resolution
* process. Method resolution (which translates from a method name in an expression to a real
* method to invoke) will normally retrieve candidate methods for invocation via a simple call
* to 'Class.getMethods()' and will choose the first one that is suitable for the
* input parameters. By registering a MethodFilter the user can receive a callback
* and change the methods that will be considered suitable.
* MethodFilter instances allow SpEL users to fine tune the behaviour of the method
* resolution process. Method resolution (which translates from a method name in an
* expression to a real method to invoke) will normally retrieve candidate methods for
* invocation via a simple call to 'Class.getMethods()' and will choose the first one that
* is suitable for the input parameters. By registering a MethodFilter the user can
* receive a callback and change the methods that will be considered suitable.
*
* @author Andy Clement
* @since 3.0.1
@ -32,12 +33,11 @@ import java.util.List;
public interface MethodFilter {
/**
* Called by the method resolver to allow the SpEL user to organize the list of candidate
* methods that may be invoked. The filter can remove methods that should not be
* considered candidates and it may sort the results. The resolver will then search
* through the methods as returned from the filter when looking for a suitable
* Called by the method resolver to allow the SpEL user to organize the list of
* candidate methods that may be invoked. The filter can remove methods that should
* not be considered candidates and it may sort the results. The resolver will then
* search through the methods as returned from the filter when looking for a suitable
* candidate to invoke.
*
* @param methods the full list of methods the resolver was going to choose from
* @return a possible subset of input methods that may be sorted by order of relevance
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,8 +21,9 @@ import java.util.List;
import org.springframework.core.convert.TypeDescriptor;
/**
* A method resolver attempts locate a method and returns a command executor that can be used to invoke that method.
* The command executor will be cached but if it 'goes stale' the resolvers will be called again.
* A method resolver attempts locate a method and returns a command executor that can be
* used to invoke that method. The command executor will be cached but if it 'goes stale'
* the resolvers will be called again.
*
* @author Andy Clement
* @since 3.0
@ -30,13 +31,14 @@ import org.springframework.core.convert.TypeDescriptor;
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 {@code null} if no method could be found).
* 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 {@code 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
* @return a MethodExecutor that can invoke the method, or null if the method cannot be found
* @return a MethodExecutor that can invoke the method, or null if the method cannot
* be found
*/
MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,
List<TypeDescriptor> argumentTypes) throws AccessException;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,13 +17,24 @@
package org.springframework.expression;
/**
* Supported operations that an {@link OperatorOverloader} can implement for any pair of operands.
* 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, POWER
ADD,
SUBTRACT,
DIVIDE,
MULTIPLY,
MODULUS,
POWER
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,8 +17,9 @@
package org.springframework.expression;
/**
* By default the mathematical operators {@link Operation} support simple types like numbers. By providing an
* implementation of OperatorOverloader, a user of the expression language can support these operations on other types.
* By default the mathematical operators {@link Operation} support simple types like
* numbers. By providing an implementation of OperatorOverloader, a user of the expression
* language can support these operations on other types.
*
* @author Andy Clement
* @since 3.0
@ -26,20 +27,21 @@ package org.springframework.expression;
public interface OperatorOverloader {
/**
* 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
* @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;
/**
* 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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,7 +17,8 @@
package org.springframework.expression;
/**
* Input provided to an expression parser that can influence an expression parsing/compilation routine.
* Input provided to an expression parser that can influence an expression
* parsing/compilation routine.
*
* @author Keith Donald
* @author Andy Clement
@ -26,38 +27,35 @@ package org.springframework.expression;
public interface ParserContext {
/**
* Whether or not the expression being parsed is a template. A template expression consists of literal text that can
* be mixed with evaluatable blocks. Some examples:
*
* Whether or not the expression being parsed is a template. A template expression
* consists of literal text that can be mixed with evaluatable blocks. Some examples:
* <pre class="code">
* Some literal text
* Hello #{name.firstName}!
* #{3 + 4}
* </pre>
*
* @return true if the expression is a template, false otherwise
*/
boolean isTemplate();
/**
* For template expressions, returns the prefix that identifies the start of an expression block within a string.
* For example: "${"
*
* For template expressions, returns the prefix that identifies the start of an
* expression block within a string. For example: "${"
* @return the prefix that identifies the start of an expression
*/
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
*/
String getExpressionSuffix();
/**
* The default ParserContext implementation that enables template expression parsing mode.
* The expression prefix is #{ and the expression suffix is }.
* The default ParserContext implementation that enables template expression parsing
* mode. The expression prefix is #{ and the expression suffix is }.
* @see #isTemplate()
*/
public static final ParserContext TEMPLATE_EXPRESSION = new ParserContext() {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,13 +18,15 @@ package org.springframework.expression;
/**
* A property accessor is able to read (and possibly write) to object properties. The interface places no restrictions
* and so implementors are free to access properties directly as fields or through getters or in any other way they see
* as appropriate. A resolver can optionally specify an array of target classes for which it should be called - but if
* it returns null from getSpecificTargetClasses() then it will be called for all property references and given a chance
* 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.
* A property accessor is able to read (and possibly write) to object properties. The
* interface places no restrictions and so implementors are free to access properties
* directly as fields or through getters or in any other way they see as appropriate. A
* resolver can optionally specify an array of target classes for which it should be
* called - but if it returns null from getSpecificTargetClasses() then it will be called
* for all property references and given a chance 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.
*
* @author Andy Clement
* @since 3.0
@ -32,19 +34,23 @@ package org.springframework.expression;
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)
* 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)
*/
Class[] getSpecificTargetClasses();
/**
* Called to determine if a resolver instance is able to access a specified property on a specified target object.
* 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
* @throws AccessException if there is any problem determining whether the property
* can be read
*/
boolean canRead(EvaluationContext context, Object target, String name) throws AccessException;
@ -59,17 +65,20 @@ public interface PropertyAccessor {
TypedValue 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.
* 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
* @throws AccessException if there is any problem determining whether the property
* can be written to
*/
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.
* 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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,8 +17,8 @@
package org.springframework.expression;
/**
* Instances of a type comparator should be able to compare pairs of objects for equality, the specification of the
* return value is the same as for {@link Comparable}.
* Instances of a type comparator should be able to compare pairs of objects for equality,
* the specification of the return value is the same as for {@link Comparable}.
*
* @author Andy Clement
* @since 3.0
@ -29,9 +29,10 @@ 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
* second
* @throws EvaluationException if a problem occurs during comparison (or they are not comparable)
* @return 0 if they are equal, <0 if the first is smaller than the second, or >0 if
* the first is larger than the second
* @throws EvaluationException if a problem occurs during comparison (or they are not
* comparable)
*/
int compare(Object firstObject, Object secondObject) throws EvaluationException;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,10 +19,10 @@ package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
* A type converter can convert values between different types encountered
* during expression evaluation. This is an SPI for the expression parser;
* see {@link org.springframework.core.convert.ConversionService} for the
* primary user API to Spring's conversion facilities.
* A type converter can convert values between different types encountered during
* expression evaluation. This is an SPI for the expression parser; see
* {@link org.springframework.core.convert.ConversionService} for the primary user API to
* Spring's conversion facilities.
*
* @author Andy Clement
* @author Juergen Hoeller
@ -31,7 +31,8 @@ import org.springframework.core.convert.TypeDescriptor;
public interface TypeConverter {
/**
* Return true if the type converter can convert the specified type to the desired target type.
* Return true if the type converter can convert the specified type to the desired
* target type.
* @param sourceType a type descriptor that describes the source type
* @param targetType a type descriptor that describes the requested result type
* @return true if that conversion can be performed
@ -39,12 +40,15 @@ public interface TypeConverter {
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* Convert (may coerce) a value from one type to another, for example from a boolean to a string.
* The typeDescriptor parameter enables support for typed collections - if the caller really wishes they
* can have a List&lt;Integer&gt; for example, rather than simply a List.
* Convert (may coerce) a value from one type to another, for example from a boolean
* to a string. The typeDescriptor parameter enables support for typed collections -
* if the caller really wishes they can have a List&lt;Integer&gt; for example, rather
* than simply a List.
* @param value the value to be converted
* @param sourceType a type descriptor that supplies extra information about the source object
* @param targetType a type descriptor that supplies extra information about the requested result type
* @param sourceType a type descriptor that supplies extra information about the
* source object
* @param targetType a type descriptor that supplies extra information about the
* requested result type
* @return the converted value
* @throws EvaluationException if conversion is not possible
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,9 +17,11 @@
package org.springframework.expression;
/**
* 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.
* 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
@ -27,7 +29,8 @@ package org.springframework.expression;
public interface TypeLocator {
/**
* Find a type by name. The name may or may not be fully qualified (eg. String or java.lang.String)
* Find a type by name. The name may or may not be fully qualified (eg. String or
* java.lang.String)
* @param typename the type to be located
* @return the class object representing that type
* @throws EvaluationException if there is a problem finding it

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,9 +19,9 @@ package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
* Encapsulates an object and a type descriptor that describes it.
* The type descriptor can hold generic information that would not be
* accessible through a simple {@code getClass()} call on the object.
* Encapsulates an object and a type descriptor that describes it. The type descriptor can
* hold generic information that would not be accessible through a simple
* {@code getClass()} call on the object.
*
* @author Andy Clement
* @author Juergen Hoeller

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,18 +20,21 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.TypedValue;
/**
* 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:
*
* 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:
*
* <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()} when invoked.
*
* &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()} when invoked.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
@ -131,13 +134,13 @@ public class CompositeStringExpression implements Expression {
@Override
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
Object value = getValue(context);
return ExpressionUtils.convert(context, value, expectedResultType);
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
}
@Override
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
Object value = getValue();
return ExpressionUtils.convert(null, value, expectedResultType);
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
}
@Override
@ -146,21 +149,21 @@ public class CompositeStringExpression implements Expression {
}
public Expression[] getExpressions() {
return expressions;
return this.expressions;
}
@Override
public <T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException {
Object value = getValue(rootObject);
return ExpressionUtils.convert(null, value, desiredResultType);
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
}
@Override
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType)
throws EvaluationException {
Object value = getValue(context,rootObject);
return ExpressionUtils.convert(context, value, desiredResultType);
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), desiredResultType);
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,29 +33,32 @@ import org.springframework.util.ClassUtils;
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.
* 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 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
* @throws EvaluationException if there is a problem during conversion or conversion
* of the value to the specified type is not supported
* @deprecated use {@link #convertTypedValue(EvaluationContext, TypedValue, Class)}
*/
@Deprecated
public static <T> T convert(EvaluationContext context, Object value, Class<T> targetType) throws EvaluationException {
// TODO remove this function over time and use the one it delegates to
return convertTypedValue(context,new TypedValue(value),targetType);
return convertTypedValue(context, new TypedValue(value), targetType);
}
/**
* 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.
* 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 typedValue the value to convert and a type descriptor describing it
* @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
* @throws EvaluationException if there is a problem during conversion or conversion
* of the value to the specified type is not supported
*/
@SuppressWarnings("unchecked")
public static <T> T convertTypedValue(EvaluationContext context, TypedValue typedValue, Class<T> targetType) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,13 +20,14 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.TypedValue;
/**
* 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.
*
* 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
*/
@ -78,19 +79,19 @@ public class LiteralExpression implements Expression {
@Override
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
throw new EvaluationException(this.literalValue, "Cannot call setValue() on a LiteralExpression");
}
@Override
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
Object value = getValue(context);
return ExpressionUtils.convert(context, value, expectedResultType);
return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
}
@Override
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
Object value = getValue();
return ExpressionUtils.convert(null, value, expectedResultType);
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
}
@Override
@ -106,7 +107,7 @@ public class LiteralExpression implements Expression {
@Override
public <T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException {
Object value = getValue(rootObject);
return ExpressionUtils.convert(null, value, desiredResultType);
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
}
@Override
@ -117,7 +118,7 @@ public class LiteralExpression implements Expression {
@Override
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType) throws EvaluationException {
Object value = getValue(context, rootObject);
return ExpressionUtils.convert(null, value, desiredResultType);
return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
}
@Override
@ -147,7 +148,7 @@ public class LiteralExpression implements Expression {
@Override
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
throw new EvaluationException(this.literalValue, "Cannot call setValue() on a LiteralExpression");
}
@Override
@ -157,7 +158,7 @@ public class LiteralExpression implements Expression {
@Override
public void setValue(Object rootObject, Object value) throws EvaluationException {
throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
throw new EvaluationException(this.literalValue, "Cannot call setValue() on a LiteralExpression");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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,9 +26,9 @@ import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
/**
* An expression parser that understands templates. It can be subclassed
* by expression parsers that do not offer first class support for templating.
*
* An expression parser that understands templates. It can be subclassed by expression
* parsers that do not offer first class support for templating.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Andy Clement
@ -40,102 +40,124 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
* Default ParserContext instance for non-template expressions.
*/
private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
@Override
public String getExpressionPrefix() {
return null;
}
@Override
public String getExpressionSuffix() {
return null;
}
@Override
public boolean isTemplate() {
return false;
}
};
@Override
public Expression parseExpression(String expressionString) throws ParseException {
return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
}
@Override
public Expression parseExpression(String expressionString, ParserContext context) throws ParseException {
public Expression parseExpression(String expressionString, ParserContext context)
throws ParseException {
if (context == null) {
context = NON_TEMPLATE_PARSER_CONTEXT;
}
if (context.isTemplate()) {
return parseTemplate(expressionString, context);
} else {
}
else {
return doParseExpression(expressionString, context);
}
}
private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException {
private Expression parseTemplate(String expressionString, ParserContext context)
throws ParseException {
if (expressionString.length() == 0) {
return new LiteralExpression("");
}
Expression[] expressions = parseExpressions(expressionString, context);
if (expressions.length == 1) {
return expressions[0];
} else {
}
else {
return new CompositeStringExpression(expressionString, expressions);
}
}
/**
* Helper that parses given expression string using the configured parser. The expression string can contain any
* number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static
* pieces of text will also be returned as Expressions that just return that static piece of text. As a result,
* evaluating all returned expressions and concatenating the results produces the complete evaluated string.
* Unwrapping is only done of the outermost delimiters found, so the string 'hello ${foo${abc}}' would break into
* the pieces 'hello ' and 'foo${abc}'. This means that expression languages that used ${..} as part of their
* functionality are supported without any problem.
* The parsing is aware of the structure of an embedded expression. It assumes that parentheses '(',
* square brackets '[' and curly brackets '}' must be in pairs within the expression unless they are within a
* string literal and a string literal starts and terminates with a single quote '.
*
* Helper that parses given expression string using the configured parser. The
* expression string can contain any number of expressions all contained in "${...}"
* markers. For instance: "foo${expr0}bar${expr1}". The static pieces of text will
* also be returned as Expressions that just return that static piece of text. As a
* result, evaluating all returned expressions and concatenating the results produces
* the complete evaluated string. Unwrapping is only done of the outermost delimiters
* found, so the string 'hello ${foo${abc}}' would break into the pieces 'hello ' and
* 'foo${abc}'. This means that expression languages that used ${..} as part of their
* functionality are supported without any problem. The parsing is aware of the
* structure of an embedded expression. It assumes that parentheses '(', square
* brackets '[' and curly brackets '}' must be in pairs within the expression unless
* they are within a string literal and a string literal starts and terminates with a
* single quote '.
* @param expressionString the expression string
* @return the parsed expressions
* @throws ParseException when the expressions cannot be parsed
*/
private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException {
private Expression[] parseExpressions(String expressionString, ParserContext context)
throws ParseException {
List<Expression> expressions = new LinkedList<Expression>();
String prefix = context.getExpressionPrefix();
String suffix = context.getExpressionSuffix();
int startIdx = 0;
while (startIdx < expressionString.length()) {
int prefixIndex = expressionString.indexOf(prefix,startIdx);
int prefixIndex = expressionString.indexOf(prefix, startIdx);
if (prefixIndex >= startIdx) {
// an inner expression was found - this is a composite
if (prefixIndex > startIdx) {
expressions.add(createLiteralExpression(context,expressionString.substring(startIdx, prefixIndex)));
expressions.add(createLiteralExpression(context,
expressionString.substring(startIdx, prefixIndex)));
}
int afterPrefixIndex = prefixIndex + prefix.length();
int suffixIndex = skipToCorrectEndSuffix(prefix,suffix,expressionString,afterPrefixIndex);
int suffixIndex = skipToCorrectEndSuffix(prefix, suffix,
expressionString, afterPrefixIndex);
if (suffixIndex == -1) {
throw new ParseException(expressionString, prefixIndex, "No ending suffix '" + suffix +
"' for expression starting at character " + prefixIndex + ": " +
expressionString.substring(prefixIndex));
throw new ParseException(expressionString, prefixIndex,
"No ending suffix '" + suffix
+ "' for expression starting at character "
+ prefixIndex + ": "
+ expressionString.substring(prefixIndex));
}
if (suffixIndex == afterPrefixIndex) {
throw new ParseException(expressionString, prefixIndex, "No expression defined within delimiter '" +
prefix + suffix + "' at character " + prefixIndex);
} else {
String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
expr = expr.trim();
if (expr.length()==0) {
throw new ParseException(expressionString, prefixIndex, "No expression defined within delimiter '" +
prefix + suffix + "' at character " + prefixIndex);
}
expressions.add(doParseExpression(expr, context));
startIdx = suffixIndex + suffix.length();
throw new ParseException(expressionString, prefixIndex,
"No expression defined within delimiter '" + prefix + suffix
+ "' at character " + prefixIndex);
}
} else {
String expr = expressionString.substring(prefixIndex + prefix.length(),
suffixIndex);
expr = expr.trim();
if (expr.length() == 0) {
throw new ParseException(expressionString, prefixIndex,
"No expression defined within delimiter '" + prefix + suffix
+ "' at character " + prefixIndex);
}
expressions.add(doParseExpression(expr, context));
startIdx = suffixIndex + suffix.length();
}
else {
// no more ${expressions} found in string, add rest as static text
expressions.add(createLiteralExpression(context,expressionString.substring(startIdx)));
expressions.add(createLiteralExpression(context,
expressionString.substring(startIdx)));
startIdx = expressionString.length();
}
}
@ -147,19 +169,20 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
}
/**
* Return true if the specified suffix can be found at the supplied position in the supplied expression string.
* Return true if the specified suffix can be found at the supplied position in the
* supplied expression string.
* @param expressionString the expression string which may contain the suffix
* @param pos the start position at which to check for the suffix
* @param suffix the suffix string
*/
private boolean isSuffixHere(String expressionString,int pos,String suffix) {
private boolean isSuffixHere(String expressionString, int pos, String suffix) {
int suffixPosition = 0;
for (int i=0;i<suffix.length() && pos<expressionString.length();i++) {
if (expressionString.charAt(pos++)!=suffix.charAt(suffixPosition++)) {
for (int i = 0; i < suffix.length() && pos < expressionString.length(); i++) {
if (expressionString.charAt(pos++) != suffix.charAt(suffixPosition++)) {
return false;
}
}
if (suffixPosition!=suffix.length()) {
if (suffixPosition != suffix.length()) {
// the expressionString ran out before the suffix could entirely be found
return false;
}
@ -167,58 +190,73 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
}
/**
* Copes with nesting, for example '${...${...}}' where the correct end for the first ${ is the final }.
*
* Copes with nesting, for example '${...${...}}' where the correct end for the first
* ${ is the final }.
* @param prefix the prefix
* @param suffix the suffix
* @param expressionString the expression string
* @param afterPrefixIndex the most recently found prefix location for which the matching end suffix is being sought
* @param afterPrefixIndex the most recently found prefix location for which the
* matching end suffix is being sought
* @return the position of the correct matching nextSuffix or -1 if none can be found
*/
private int skipToCorrectEndSuffix(String prefix, String suffix, String expressionString, int afterPrefixIndex) throws ParseException {
private int skipToCorrectEndSuffix(String prefix, String suffix,
String expressionString, int afterPrefixIndex) throws ParseException {
// Chew on the expression text - relying on the rules:
// brackets must be in pairs: () [] {}
// string literals are "..." or '...' and these may contain unmatched brackets
int pos = afterPrefixIndex;
int maxlen = expressionString.length();
int nextSuffix = expressionString.indexOf(suffix,afterPrefixIndex);
if (nextSuffix ==-1 ) {
int nextSuffix = expressionString.indexOf(suffix, afterPrefixIndex);
if (nextSuffix == -1) {
return -1; // the suffix is missing
}
Stack<Bracket> stack = new Stack<Bracket>();
while (pos<maxlen) {
if (isSuffixHere(expressionString,pos,suffix) && stack.isEmpty()) {
while (pos < maxlen) {
if (isSuffixHere(expressionString, pos, suffix) && stack.isEmpty()) {
break;
}
char ch = expressionString.charAt(pos);
switch (ch) {
case '{': case '[': case '(':
stack.push(new Bracket(ch,pos));
break;
case '}':case ']':case ')':
if (stack.isEmpty()) {
throw new ParseException(expressionString, pos, "Found closing '"+ch+"' at position "+pos+" without an opening '"+Bracket.theOpenBracketFor(ch)+"'");
}
Bracket p = stack.pop();
if (!p.compatibleWithCloseBracket(ch)) {
throw new ParseException(expressionString, pos, "Found closing '"+ch+"' at position "+pos+" but most recent opening is '"+p.bracket+"' at position "+p.pos);
}
break;
case '\'':
case '"':
// jump to the end of the literal
int endLiteral = expressionString.indexOf(ch,pos+1);
if (endLiteral==-1) {
throw new ParseException(expressionString, pos, "Found non terminating string literal starting at position "+pos);
}
pos=endLiteral;
break;
case '{':
case '[':
case '(':
stack.push(new Bracket(ch, pos));
break;
case '}':
case ']':
case ')':
if (stack.isEmpty()) {
throw new ParseException(expressionString, pos, "Found closing '"
+ ch + "' at position " + pos + " without an opening '"
+ Bracket.theOpenBracketFor(ch) + "'");
}
Bracket p = stack.pop();
if (!p.compatibleWithCloseBracket(ch)) {
throw new ParseException(expressionString, pos, "Found closing '"
+ ch + "' at position " + pos
+ " but most recent opening is '" + p.bracket
+ "' at position " + p.pos);
}
break;
case '\'':
case '"':
// jump to the end of the literal
int endLiteral = expressionString.indexOf(ch, pos + 1);
if (endLiteral == -1) {
throw new ParseException(expressionString, pos,
"Found non terminating string literal starting at position "
+ pos);
}
pos = endLiteral;
break;
}
pos++;
}
if (!stack.isEmpty()) {
Bracket p = stack.pop();
throw new ParseException(expressionString, p.pos, "Missing closing '"+Bracket.theCloseBracketFor(p.bracket)+"' for '"+p.bracket+"' at position "+p.pos);
throw new ParseException(expressionString, p.pos, "Missing closing '"
+ Bracket.theCloseBracketFor(p.bracket) + "' for '" + p.bracket
+ "' at position " + p.pos);
}
if (!isSuffixHere(expressionString, pos, suffix)) {
return -1;
@ -226,43 +264,49 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
return pos;
}
/**
* This captures a type of bracket and the position in which it occurs in the expression. The positional
* information is used if an error has to be reported because the related end bracket cannot be found.
* Bracket is used to describe: square brackets [] round brackets () and curly brackets {}
* This captures a type of bracket and the position in which it occurs in the
* expression. The positional information is used if an error has to be reported
* because the related end bracket cannot be found. Bracket is used to describe:
* square brackets [] round brackets () and curly brackets {}
*/
private static class Bracket {
char bracket;
int pos;
Bracket(char bracket,int pos) {
Bracket(char bracket, int pos) {
this.bracket = bracket;
this.pos = pos;
}
boolean compatibleWithCloseBracket(char closeBracket) {
if (bracket=='{') {
return closeBracket=='}';
} else if (bracket=='[') {
return closeBracket==']';
if (this.bracket == '{') {
return closeBracket == '}';
}
return closeBracket==')';
else if (this.bracket == '[') {
return closeBracket == ']';
}
return closeBracket == ')';
}
static char theOpenBracketFor(char closeBracket) {
if (closeBracket=='}') {
if (closeBracket == '}') {
return '{';
} else if (closeBracket==']') {
}
else if (closeBracket == ']') {
return '[';
}
return '(';
}
static char theCloseBracketFor(char openBracket) {
if (openBracket=='{') {
if (openBracket == '{') {
return '}';
} else if (openBracket=='[') {
}
else if (openBracket == '[') {
return ']';
}
return ')';
@ -276,8 +320,7 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
* @return an evaluator for the parsed expression
* @throws ParseException an exception occurred during parsing
*/
protected abstract Expression doParseExpression(String expressionString, ParserContext context)
throws ParseException;
protected abstract Expression doParseExpression(String expressionString,
ParserContext context) throws ParseException;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,8 +19,8 @@ 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.
* Configurable {@link ParserContext} implementation for template parsing. Expects the
* expression prefix and suffix as constructor arguments.
*
* @author Juergen Hoeller
* @since 3.0

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,12 +32,15 @@ import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
/**
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other
* expressions but it gives a place to hold local variables and for component expressions in a compound expression to
* communicate state. This is in contrast to the EvaluationContext, which is shared amongst expression evaluations, and
* any changes to it will be seen by other expressions or any code that chooses to ask questions of the context.
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to
* it are not seen by other expressions but it gives a place to hold local variables and
* for component expressions in a compound expression to communicate state. This is in
* contrast to the EvaluationContext, which is shared amongst expression evaluations, and
* any changes to it will be seen by other expressions or any code that chooses to ask
* questions of the context.
*
* <p>It also acts as a place for to define common utility routines that the various Ast nodes might need.
* <p>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
@ -138,7 +141,8 @@ public class ExpressionState {
}
public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
return this.relatedContext.getTypeConverter().convertValue(value, TypeDescriptor.forObject(value), targetTypeDescriptor);
return this.relatedContext.getTypeConverter().convertValue(value,
TypeDescriptor.forObject(value), targetTypeDescriptor);
}
public TypeConverter getTypeConverter() {
@ -147,7 +151,8 @@ public class ExpressionState {
public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
Object val = value.getValue();
return this.relatedContext.getTypeConverter().convertValue(val, TypeDescriptor.forObject(val), targetTypeDescriptor);
return this.relatedContext.getTypeConverter().convertValue(val,
TypeDescriptor.forObject(val), targetTypeDescriptor);
}
/*
@ -210,6 +215,7 @@ public class ExpressionState {
return this.configuration;
}
/**
* 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
@ -221,6 +227,7 @@ public class ExpressionState {
public VariableScope() { }
public VariableScope(Map<String, Object> arguments) {
if (arguments != null) {
this.vars.putAll(arguments);
@ -231,6 +238,7 @@ public class ExpressionState {
this.vars.put(name,value);
}
public Object lookupVariable(String name) {
return this.vars.get(name);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,9 +18,9 @@ package org.springframework.expression.spel;
import org.springframework.expression.EvaluationException;
/**
* Root exception for Spring EL related exceptions. Rather than holding a hard coded string indicating the problem, it
* records a message key and the inserts for the message. See {@link SpelMessage} for the list of all possible messages
* that can occur.
* Root exception for Spring EL related exceptions. Rather than holding a hard coded
* string indicating the problem, it records a message key and the inserts for the
* message. See {@link SpelMessage} for the list of all possible messages that can occur.
*
* @author Andy Clement
* @since 3.0
@ -28,8 +28,10 @@ import org.springframework.expression.EvaluationException;
@SuppressWarnings("serial")
public class SpelEvaluationException extends EvaluationException {
private SpelMessage message;
private Object[] inserts;
private final SpelMessage message;
private final Object[] inserts;
public SpelEvaluationException(SpelMessage message, Object... inserts) {
super(message.formatMessage(0, inserts)); // TODO poor position information, can the callers not really supply something?
@ -56,15 +58,18 @@ public class SpelEvaluationException extends EvaluationException {
this.inserts = inserts;
}
/**
* @return a formatted message with inserts applied
*/
@Override
public String getMessage() {
if (message != null)
return message.formatMessage(position, inserts);
else
if (this.message != null) {
return this.message.formatMessage(this.position, this.inserts);
}
else {
return super.getMessage();
}
}
/**
@ -87,7 +92,7 @@ public class SpelEvaluationException extends EvaluationException {
* @return the message inserts
*/
public Object[] getInserts() {
return inserts;
return this.inserts;
}
}

View File

@ -19,98 +19,249 @@ package org.springframework.expression.spel;
import java.text.MessageFormat;
/**
* Contains all the messages that can be produced by the Spring Expression Language. Each message has a kind (info,
* warn, error) and a code number. Tests can be written to expect particular code numbers rather than particular text,
* enabling the message text to more easily be modified and the tests to run successfully in different locales.
* <p>
* When a message is formatted, it will have this kind of form
* Contains all the messages that can be produced by the Spring Expression Language. Each
* message has a kind (info, warn, error) and a code number. Tests can be written to
* expect particular code numbers rather than particular text, enabling the message text
* to more easily be modified and the tests to run successfully in different locales.
*
* <p>When a message is formatted, it will have this kind of form
*
* <pre class="code">
* EL1004E: (pos 34): Type cannot be found 'String'
* </pre>
*
* </code> The prefix captures the code and the error kind, whilst the position is included if it is known.
* The prefix captures the code and the error kind, whilst the position is
* included if it is known.
*
* @author Andy Clement
* @since 3.0
*/
public enum SpelMessage {
TYPE_CONVERSION_ERROR(Kind.ERROR, 1001, "Type conversion problem, cannot convert from {0} to {1}"), //
CONSTRUCTOR_NOT_FOUND(Kind.ERROR, 1002, "Constructor call: No suitable constructor found on type {0} for arguments {1}"), //
CONSTRUCTOR_INVOCATION_PROBLEM(Kind.ERROR, 1003, "A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"), //
METHOD_NOT_FOUND(Kind.ERROR, 1004, "Method call: Method {0} cannot be found on {1} type"), //
TYPE_NOT_FOUND(Kind.ERROR, 1005, "Type cannot be found ''{0}''"), //
FUNCTION_NOT_DEFINED(Kind.ERROR, 1006, "The function ''{0}'' could not be found"), //
PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL(Kind.ERROR, 1007, "Field or property ''{0}'' cannot be found on null"), //
PROPERTY_OR_FIELD_NOT_READABLE(Kind.ERROR, 1008, "Field or property ''{0}'' cannot be found on object of type ''{1}''"), //
PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL(Kind.ERROR, 1009, "Field or property ''{0}'' cannot be set on null"), //
PROPERTY_OR_FIELD_NOT_WRITABLE(Kind.ERROR, 1010, "Field or property ''{0}'' cannot be set on object of type ''{1}''"), //
METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1011, "Method call: Attempted to call method {0} on null context object"), //
CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1012, "Cannot index into a null value"),
NOT_COMPARABLE(Kind.ERROR, 1013, "Cannot compare instances of {0} and {1}"), //
INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(Kind.ERROR, 1014, "Incorrect number of arguments for function, {0} supplied but function takes {1}"), //
INVALID_TYPE_FOR_SELECTION(Kind.ERROR, 1015, "Cannot perform selection on input data of type ''{0}''"), //
RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(Kind.ERROR, 1016, "Result of selection criteria is not boolean"), //
BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(Kind.ERROR, 1017, "Right operand for the 'between' operator has to be a two-element list"), //
INVALID_PATTERN(Kind.ERROR, 1018, "Pattern is not valid ''{0}''"), //
PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1019, "Projection is not supported on the type ''{0}''"), //
ARGLIST_SHOULD_NOT_BE_EVALUATED(Kind.ERROR, 1020, "The argument list of a lambda expression should never have getValue() called upon it"), //
EXCEPTION_DURING_PROPERTY_READ(Kind.ERROR, 1021, "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"), //
FUNCTION_REFERENCE_CANNOT_BE_INVOKED(Kind.ERROR, 1022, "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"), //
EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR, 1023, "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"), //
ARRAY_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1024, "The array has ''{0}'' elements, index ''{1}'' is invalid"), //
COLLECTION_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1025, "The collection has ''{0}'' elements, index ''{1}'' is invalid"), //
STRING_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1026, "The string has ''{0}'' characters, index ''{1}'' is invalid"), //
INDEXING_NOT_SUPPORTED_FOR_TYPE(Kind.ERROR, 1027, "Indexing into type ''{0}'' is not supported"), //
INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(Kind.ERROR, 1028, "The operator 'instanceof' needs the right operand to be a class, not a ''{0}''"), //
EXCEPTION_DURING_METHOD_INVOCATION(Kind.ERROR, 1029, "A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"), //
OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(Kind.ERROR, 1030, "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"), //
PROBLEM_LOCATING_METHOD(Kind.ERROR, 1031, "Problem locating method {0} cannot on type {1}"),
SETVALUE_NOT_SUPPORTED( Kind.ERROR, 1032, "setValue(ExpressionState, Object) not supported for ''{0}''"), //
MULTIPLE_POSSIBLE_METHODS(Kind.ERROR, 1033, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"), //
EXCEPTION_DURING_PROPERTY_WRITE(Kind.ERROR, 1034, "A problem occurred whilst attempting to set the property ''{0}'': {1}"), //
NOT_AN_INTEGER(Kind.ERROR, 1035, "The value ''{0}'' cannot be parsed as an int"), //
NOT_A_LONG(Kind.ERROR, 1036, "The value ''{0}'' cannot be parsed as a long"), //
INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1037, "First operand to matches operator must be a string. ''{0}'' is not"), //
INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1038, "Second operand to matches operator must be a string. ''{0}'' is not"), //
FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1039, "Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static."),//
NOT_A_REAL(Kind.ERROR, 1040, "The value ''{0}'' cannot be parsed as a double"), //
MORE_INPUT(Kind.ERROR,1041, "After parsing a valid expression, there is still more data in the expression: ''{0}''"),
RIGHT_OPERAND_PROBLEM(Kind.ERROR,1042, "Problem parsing right operand"),
NOT_EXPECTED_TOKEN(Kind.ERROR,1043,"Unexpected token. Expected ''{0}'' but was ''{1}''"),
OOD(Kind.ERROR,1044,"Unexpectedly ran out of input"), //
NON_TERMINATING_DOUBLE_QUOTED_STRING(Kind.ERROR,1045,"Cannot find terminating \" for string"),//
NON_TERMINATING_QUOTED_STRING(Kind.ERROR,1046,"Cannot find terminating ' for string"), //
MISSING_LEADING_ZERO_FOR_NUMBER(Kind.ERROR,1047,"A real number must be prefixed by zero, it cannot start with just ''.''"), //
REAL_CANNOT_BE_LONG(Kind.ERROR,1048,"Real number cannot be suffixed with a long (L or l) suffix"),//
UNEXPECTED_DATA_AFTER_DOT(Kind.ERROR,1049,"Unexpected data after ''.'': ''{0}''"),//
MISSING_CONSTRUCTOR_ARGS(Kind.ERROR,1050,"The arguments '(...)' for the constructor call are missing"),//
RUN_OUT_OF_ARGUMENTS(Kind.ERROR,1051,"Unexpected ran out of arguments"),//
UNABLE_TO_GROW_COLLECTION(Kind.ERROR,1052,"Unable to grow collection"),//
UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE(Kind.ERROR,1053,"Unable to grow collection: unable to determine list element type"),//
UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR,1054,"Unable to dynamically create a List to replace a null value"),//
UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR,1055,"Unable to dynamically create a Map to replace a null value"),//
UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR,1056,"Unable to dynamically create instance of ''{0}'' to replace a null value"),//
NO_BEAN_RESOLVER_REGISTERED(Kind.ERROR,1057,"No bean resolver registered in the context to resolve access to bean ''{0}''"),//
EXCEPTION_DURING_BEAN_RESOLUTION(Kind.ERROR, 1058, "A problem occurred when trying to resolve bean ''{0}'':''{1}''"), //
INVALID_BEAN_REFERENCE(Kind.ERROR,1059,"@ can only be followed by an identifier or a quoted name"),//
TYPE_CONVERSION_ERROR(Kind.ERROR, 1001,
"Type conversion problem, cannot convert from {0} to {1}"),
CONSTRUCTOR_NOT_FOUND(Kind.ERROR, 1002,
"Constructor call: No suitable constructor found on type {0} for " +
"arguments {1}"),
CONSTRUCTOR_INVOCATION_PROBLEM(Kind.ERROR, 1003,
"A problem occurred whilst attempting to construct an object of type " +
"''{0}'' using arguments ''{1}''"),
METHOD_NOT_FOUND(Kind.ERROR, 1004,
"Method call: Method {0} cannot be found on {1} type"),
TYPE_NOT_FOUND(Kind.ERROR, 1005,
"Type cannot be found ''{0}''"),
FUNCTION_NOT_DEFINED(Kind.ERROR, 1006,
"The function ''{0}'' could not be found"),
PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL(Kind.ERROR, 1007,
"Field or property ''{0}'' cannot be found on null"),
PROPERTY_OR_FIELD_NOT_READABLE(Kind.ERROR, 1008,
"Field or property ''{0}'' cannot be found on object of type ''{1}''"),
PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL(Kind.ERROR, 1009,
"Field or property ''{0}'' cannot be set on null"),
PROPERTY_OR_FIELD_NOT_WRITABLE(Kind.ERROR, 1010,
"Field or property ''{0}'' cannot be set on object of type ''{1}''"),
METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1011,
"Method call: Attempted to call method {0} on null context object"),
CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1012,
"Cannot index into a null value"),
NOT_COMPARABLE(Kind.ERROR, 1013,
"Cannot compare instances of {0} and {1}"),
INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(Kind.ERROR, 1014,
"Incorrect number of arguments for function, {0} supplied but " +
"function takes {1}"),
INVALID_TYPE_FOR_SELECTION(Kind.ERROR, 1015,
"Cannot perform selection on input data of type ''{0}''"),
RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(Kind.ERROR, 1016,
"Result of selection criteria is not boolean"),
BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(Kind.ERROR, 1017,
"Right operand for the 'between' operator has to be a two-element list"),
INVALID_PATTERN(Kind.ERROR, 1018,
"Pattern is not valid ''{0}''"),
PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1019,
"Projection is not supported on the type ''{0}''"),
ARGLIST_SHOULD_NOT_BE_EVALUATED(Kind.ERROR, 1020,
"The argument list of a lambda expression should never have getValue() " +
"called upon it"),
EXCEPTION_DURING_PROPERTY_READ(Kind.ERROR, 1021,
"A problem occurred whilst attempting to access the property " +
"''{0}'': ''{1}''"),
FUNCTION_REFERENCE_CANNOT_BE_INVOKED(Kind.ERROR, 1022,
"The function ''{0}'' mapped to an object of type ''{1}'' which " +
"cannot be invoked"),
EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR, 1023,
"A problem occurred whilst attempting to invoke the " +
"function ''{0}'': ''{1}''"),
ARRAY_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1024,
"The array has ''{0}'' elements, index ''{1}'' is invalid"),
COLLECTION_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1025,
"The collection has ''{0}'' elements, index ''{1}'' is invalid"),
STRING_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1026,
"The string has ''{0}'' characters, index ''{1}'' is invalid"),
INDEXING_NOT_SUPPORTED_FOR_TYPE(Kind.ERROR, 1027,
"Indexing into type ''{0}'' is not supported"),
INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(Kind.ERROR, 1028,
"The operator 'instanceof' needs the right operand to be a class, " +
"not a ''{0}''"),
EXCEPTION_DURING_METHOD_INVOCATION(Kind.ERROR, 1029,
"A problem occurred when trying to execute method ''{0}'' on object " +
"of type ''{1}'': ''{2}''"),
OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(Kind.ERROR, 1030,
"The operator ''{0}'' is not supported between objects of type " +
"''{1}'' and ''{2}''"),
PROBLEM_LOCATING_METHOD(Kind.ERROR, 1031,
"Problem locating method {0} cannot on type {1}"),
SETVALUE_NOT_SUPPORTED( Kind.ERROR, 1032,
"setValue(ExpressionState, Object) not supported for ''{0}''"),
MULTIPLE_POSSIBLE_METHODS(Kind.ERROR, 1033,
"Method call of ''{0}'' is ambiguous, supported type conversions " +
"allow multiple variants to match"),
EXCEPTION_DURING_PROPERTY_WRITE(Kind.ERROR, 1034,
"A problem occurred whilst attempting to set the property ''{0}'': {1}"),
NOT_AN_INTEGER(Kind.ERROR, 1035,
"The value ''{0}'' cannot be parsed as an int"),
NOT_A_LONG(Kind.ERROR, 1036,
"The value ''{0}'' cannot be parsed as a long"),
INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1037,
"First operand to matches operator must be a string. ''{0}'' is not"),
INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1038,
"Second operand to matches operator must be a string. ''{0}'' is not"),
FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1039,
"Only static methods can be called via function references. " +
"The method ''{0}'' referred to by name ''{1}'' is not static."),
NOT_A_REAL(Kind.ERROR, 1040,
"The value ''{0}'' cannot be parsed as a double"),
MORE_INPUT(Kind.ERROR,1041,
"After parsing a valid expression, there is still more data in " +
"the expression: ''{0}''"),
RIGHT_OPERAND_PROBLEM(Kind.ERROR, 1042,
"Problem parsing right operand"),
NOT_EXPECTED_TOKEN(Kind.ERROR, 1043,
"Unexpected token. Expected ''{0}'' but was ''{1}''"),
OOD(Kind.ERROR, 1044,
"Unexpectedly ran out of input"),
NON_TERMINATING_DOUBLE_QUOTED_STRING(Kind.ERROR, 1045,
"Cannot find terminating \" for string"),
NON_TERMINATING_QUOTED_STRING(Kind.ERROR, 1046,
"Cannot find terminating ' for string"),
MISSING_LEADING_ZERO_FOR_NUMBER(Kind.ERROR, 1047,
"A real number must be prefixed by zero, it cannot start with just ''.''"),
REAL_CANNOT_BE_LONG(Kind.ERROR, 1048,
"Real number cannot be suffixed with a long (L or l) suffix"),
UNEXPECTED_DATA_AFTER_DOT(Kind.ERROR, 1049,
"Unexpected data after ''.'': ''{0}''"),
MISSING_CONSTRUCTOR_ARGS(Kind.ERROR, 1050,
"The arguments '(...)' for the constructor call are missing"),
RUN_OUT_OF_ARGUMENTS(Kind.ERROR, 1051,
"Unexpected ran out of arguments"),
UNABLE_TO_GROW_COLLECTION(Kind.ERROR, 1052,
"Unable to grow collection"),
UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE(Kind.ERROR, 1053,
"Unable to grow collection: unable to determine list element type"),
UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR, 1054,
"Unable to dynamically create a List to replace a null value"),
UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR, 1055,
"Unable to dynamically create a Map to replace a null value"),
UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR, 1056,
"Unable to dynamically create instance of ''{0}'' to replace a null value"),
NO_BEAN_RESOLVER_REGISTERED(Kind.ERROR, 1057,
"No bean resolver registered in the context to resolve access to bean ''{0}''"),
EXCEPTION_DURING_BEAN_RESOLUTION(Kind.ERROR, 1058,
"A problem occurred when trying to resolve bean ''{0}'':''{1}''"),
INVALID_BEAN_REFERENCE(Kind.ERROR, 1059,
"@ can only be followed by an identifier or a quoted name"),
TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(Kind.ERROR, 1060,
"Expected the type of the new array to be specified as a String but found ''{0}''"), //
"Expected the type of the new array to be specified as a String but found ''{0}''"),
INCORRECT_ELEMENT_TYPE_FOR_ARRAY(Kind.ERROR, 1061,
"The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"), //
"The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"),
MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED(Kind.ERROR, 1062,
"Using an initializer to build a multi-dimensional array is not currently supported"), //
MISSING_ARRAY_DIMENSION(Kind.ERROR, 1063, "A required array dimension has not been specified"), //
INITIALIZER_LENGTH_INCORRECT(
Kind.ERROR, 1064, "array initializer size does not match array dimensions"), //
UNEXPECTED_ESCAPE_CHAR(Kind.ERROR,1065,"unexpected escape character."), //
OPERAND_NOT_INCREMENTABLE(Kind.ERROR,1066,"the expression component ''{0}'' does not support increment"), //
OPERAND_NOT_DECREMENTABLE(Kind.ERROR,1067,"the expression component ''{0}'' does not support decrement"), //
NOT_ASSIGNABLE(Kind.ERROR,1068,"the expression component ''{0}'' is not assignable"), //
MISSING_CHARACTER(Kind.ERROR,1069,"missing expected character ''{0}''"),
LEFT_OPERAND_PROBLEM(Kind.ERROR,1070, "Problem parsing left operand"),
MISSING_SELECTION_EXPRESSION(Kind.ERROR, 1071, "A required selection expression has not been specified");
"Using an initializer to build a multi-dimensional array is not currently supported"),
MISSING_ARRAY_DIMENSION(Kind.ERROR, 1063,
"A required array dimension has not been specified"),
INITIALIZER_LENGTH_INCORRECT(Kind.ERROR, 1064,
"array initializer size does not match array dimensions"),
UNEXPECTED_ESCAPE_CHAR(Kind.ERROR, 1065, "unexpected escape character."),
OPERAND_NOT_INCREMENTABLE(Kind.ERROR, 1066,
"the expression component ''{0}'' does not support increment"),
OPERAND_NOT_DECREMENTABLE(Kind.ERROR, 1067,
"the expression component ''{0}'' does not support decrement"),
NOT_ASSIGNABLE(Kind.ERROR, 1068,
"the expression component ''{0}'' is not assignable"),
MISSING_CHARACTER(Kind.ERROR, 1069,
"missing expected character ''{0}''"),
LEFT_OPERAND_PROBLEM(Kind.ERROR, 1070,
"Problem parsing left operand"),
MISSING_SELECTION_EXPRESSION(Kind.ERROR, 1071,
"A required selection expression has not been specified");
private Kind kind;
private int code;
@ -134,23 +285,17 @@ public enum SpelMessage {
*/
public String formatMessage(int pos, Object... inserts) {
StringBuilder formattedMessage = new StringBuilder();
formattedMessage.append("EL").append(code);
switch (kind) {
// case WARNING:
// formattedMessage.append("W");
// break;
// case INFO:
// formattedMessage.append("I");
// break;
case ERROR:
formattedMessage.append("E");
break;
formattedMessage.append("EL").append(this.code);
switch (this.kind) {
case ERROR:
formattedMessage.append("E");
break;
}
formattedMessage.append(":");
if (pos != -1) {
formattedMessage.append("(pos ").append(pos).append("): ");
}
formattedMessage.append(MessageFormat.format(message, inserts));
formattedMessage.append(MessageFormat.format(this.message, inserts));
return formattedMessage.toString();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,14 +28,16 @@ import org.springframework.expression.TypedValue;
public interface SpelNode {
/**
* Evaluate the expression node in the context of the supplied expression state and return the value.
* 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
*/
Object getValue(ExpressionState expressionState) throws EvaluationException;
/**
* Evaluate the expression node in the context of the supplied expression state and return the typed value.
* Evaluate the expression node in the context of the supplied expression state and
* return the typed value.
* @param expressionState the current expression state (includes the context)
* @return the type value of this node evaluated against the specified state
*/
@ -51,11 +53,13 @@ public interface SpelNode {
boolean isWritable(ExpressionState expressionState) throws EvaluationException;
/**
* 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.
* 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
* @throws EvaluationException if any problem occurs evaluating the expression or
* setting the new value
*/
void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException;
@ -78,7 +82,8 @@ public interface SpelNode {
/**
* Determine the class of the object passed in, unless it is already a class object.
* @param obj 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
* @return the class of the object if it is not already a class object, or null if the
* object is null
*/
Class<?> getObjectClass(Object obj);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,24 +19,20 @@ import org.springframework.expression.ParseException;
/**
* Root exception for Spring EL related exceptions. Rather than holding a hard coded string indicating the problem, it
* records a message key and the inserts for the message. See {@link SpelMessage} for the list of all possible messages
* that can occur.
*
* Root exception for Spring EL related exceptions. Rather than holding a hard coded
* string indicating the problem, it records a message key and the inserts for the
* message. See {@link SpelMessage} for the list of all possible messages that can occur.
*
* @author Andy Clement
* @since 3.0
*/
@SuppressWarnings("serial")
public class SpelParseException extends ParseException {
private SpelMessage message;
private Object[] inserts;
private final SpelMessage message;
private final Object[] inserts;
// public SpelParseException(String expressionString, int position, Throwable cause, SpelMessages message, Object... inserts) {
// super(expressionString, position, message.formatMessage(position,inserts), cause);
// this.message = message;
// this.inserts = inserts;
// }
public SpelParseException(String expressionString, int position, SpelMessage message, Object... inserts) {
super(expressionString, position, message.formatMessage(position,inserts));
@ -59,36 +55,14 @@ public class SpelParseException extends ParseException {
this.inserts = inserts;
}
//
// public SpelException(Throwable cause, SpelMessages message, Object... inserts) {
// super(cause);
// this.message = message;
// this.inserts = inserts;
// }
//
// public SpelException(int position, SpelMessages message, Object... inserts) {
// super((Throwable)null);
// this.position = position;
// this.message = message;
// this.inserts = inserts;
// }
//
// public SpelException(SpelMessages message, Object... inserts) {
// super((Throwable)null);
// this.message = message;
// this.inserts = inserts;
// }
/**
* @return a formatted message with inserts applied
*/
@Override
public String getMessage() {
if (message != null)
return message.formatMessage(position, inserts);
else
return super.getMessage();
return (this.message != null ? this.message.formatMessage(this.position, this.inserts)
: super.getMessage());
}
/**
@ -102,7 +76,7 @@ public class SpelParseException extends ParseException {
* @return the message inserts
*/
public Object[] getInserts() {
return inserts;
return this.inserts;
}
}

View File

@ -30,7 +30,7 @@ public class SpelParserConfiguration {
private final boolean autoGrowCollections;
private int maximumAutoGrowSize;
private final int maximumAutoGrowSize;
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents assignment. An alternative to calling setValue() for an expression is to use an assign.
* Represents assignment. An alternative to calling setValue() for an expression is to use
* an assign.
*
* <p>Example: 'someNumberProperty=42'
*
@ -30,21 +31,23 @@ import org.springframework.expression.spel.ExpressionState;
*/
public class Assign extends SpelNodeImpl {
public Assign(int pos,SpelNodeImpl... operands) {
super(pos,operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue newValue = children[1].getValueInternal(state);
TypedValue newValue = this.children[1].getValueInternal(state);
getChild(0).setValue(state, newValue.getValue());
return newValue;
}
@Override
public String toStringAST() {
return new StringBuilder().append(getChild(0).toStringAST()).append("=").append(getChild(1).toStringAST())
.toString();
return new StringBuilder().append(getChild(0).toStringAST()).append("=").append(
getChild(1).toStringAST()).toString();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,13 +30,15 @@ import org.springframework.expression.PropertyAccessor;
public class AstUtils {
/**
* Determines the set of property resolvers that should be used to try and access a property on the specified target
* type. The resolvers are considered to be in an ordered list, however in the returned list any that are exact
* matches for the input target type (as opposed to 'general' resolvers that could work for any type) are placed at
* the start of the list. In addition, there are specific resolvers that exactly name the class in question and
* resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the
* specific resolvers set and will be tried after exactly matching accessors but before generic accessors.
*
* Determines the set of property resolvers that should be used to try and access a
* property on the specified target type. The resolvers are considered to be in an
* ordered list, however in the returned list any that are exact matches for the input
* target type (as opposed to 'general' resolvers that could work for any type) are
* placed at the start of the list. In addition, there are specific resolvers that
* exactly name the class in question and resolvers that name a specific class but it
* is a supertype of the class we have. These are put at the end of the specific
* resolvers set and will be tried after exactly matching accessors but before generic
* accessors.
* @param targetType the type upon which property access is being attempted
* @return a list of resolvers that should be tried in order to access the property
*/

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,25 +31,31 @@ import org.springframework.expression.spel.SpelMessage;
*/
public class BeanReference extends SpelNodeImpl {
private String beanname;
private final String beanname;
public BeanReference(int pos,String beanname) {
super(pos);
this.beanname = beanname;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
BeanResolver beanResolver = state.getEvaluationContext().getBeanResolver();
if (beanResolver==null) {
throw new SpelEvaluationException(getStartPosition(),SpelMessage.NO_BEAN_RESOLVER_REGISTERED, beanname);
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.NO_BEAN_RESOLVER_REGISTERED, this.beanname);
}
try {
TypedValue bean = new TypedValue(beanResolver.resolve(state.getEvaluationContext(),beanname));
TypedValue bean = new TypedValue(beanResolver.resolve(
state.getEvaluationContext(), this.beanname));
return bean;
} catch (AccessException ae) {
}
catch (AccessException ae) {
throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,
beanname, ae.getMessage());
this.beanname, ae.getMessage());
}
}
@ -57,10 +63,11 @@ public class BeanReference extends SpelNodeImpl {
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("@");
if (beanname.indexOf('.')==-1) {
sb.append(beanname);
} else {
sb.append("'").append(beanname).append("'");
if (this.beanname.indexOf('.') == -1) {
sb.append(this.beanname);
}
else {
sb.append("'").append(this.beanname).append("'");
}
return sb.toString();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,11 +27,13 @@ public class BooleanLiteral extends Literal {
private final BooleanTypedValue value;
public BooleanLiteral(String payload, int pos, boolean value) {
super(payload, pos);
this.value = BooleanTypedValue.forValue(value);
}
@Override
public BooleanTypedValue getLiteralValue() {
return this.value;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -39,32 +39,35 @@ public class CompoundExpression extends SpelNodeImpl {
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
if (getChildCount()==1) {
return children[0].getValueRef(state);
if (getChildCount() == 1) {
return this.children[0].getValueRef(state);
}
TypedValue result = null;
SpelNodeImpl nextNode = null;
try {
nextNode = children[0];
nextNode = this.children[0];
result = nextNode.getValueInternal(state);
int cc = getChildCount();
for (int i = 1; i < cc-1; i++) {
for (int i = 1; i < cc - 1; i++) {
try {
state.pushActiveContextObject(result);
nextNode = children[i];
nextNode = this.children[i];
result = nextNode.getValueInternal(state);
} finally {
}
finally {
state.popActiveContextObject();
}
}
try {
state.pushActiveContextObject(result);
nextNode = children[cc-1];
nextNode = this.children[cc-1];
return nextNode.getValueRef(state);
} finally {
}
finally {
state.popActiveContextObject();
}
} catch (SpelEvaluationException ee) {
}
catch (SpelEvaluationException ee) {
// Correct the position for the error before re-throwing
ee.setPosition(nextNode.getStartPosition());
throw ee;
@ -96,7 +99,9 @@ public class CompoundExpression extends SpelNodeImpl {
public String toStringAST() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
if (i>0) { sb.append("."); }
if (i > 0) {
sb.append(".");
}
sb.append(getChild(i).toStringAST());
}
return sb.toString();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -132,7 +132,8 @@ public class ConstructorReference extends SpelNodeImpl {
Throwable rootCause = ae.getCause().getCause();
if (rootCause instanceof RuntimeException) {
throw (RuntimeException) rootCause;
} else {
}
else {
String typename = (String) this.children[0].getValueInternal(state).getValue();
throw new SpelEvaluationException(getStartPosition(), rootCause,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename, FormatHelper
@ -153,9 +154,9 @@ public class ConstructorReference extends SpelNodeImpl {
return executorToUse.execute(state.getEvaluationContext(), arguments);
}
catch (AccessException ae) {
throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM,
typename, FormatHelper.formatMethodForMessage("", argumentTypes));
throw new SpelEvaluationException(getStartPosition(), ae,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename,
FormatHelper.formatMethodForMessage("", argumentTypes));
}
}
@ -168,8 +169,9 @@ public class ConstructorReference extends SpelNodeImpl {
* @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
* @throws SpelEvaluationException if there is a problem locating the constructor
*/
private ConstructorExecutor findExecutorForConstructor(String typename, List<TypeDescriptor> argumentTypes,
ExpressionState state) throws SpelEvaluationException {
private ConstructorExecutor findExecutorForConstructor(String typename,
List<TypeDescriptor> argumentTypes, ExpressionState state)
throws SpelEvaluationException {
EvaluationContext eContext = state.getEvaluationContext();
List<ConstructorResolver> cResolvers = eContext.getConstructorResolvers();
@ -202,8 +204,9 @@ public class ConstructorReference extends SpelNodeImpl {
sb.append(getChild(index++).toStringAST());
sb.append("(");
for (int i = index; i < getChildCount(); i++) {
if (i > index)
if (i > index) {
sb.append(",");
}
sb.append(getChild(i).toStringAST());
}
sb.append(")");
@ -221,8 +224,8 @@ public class ConstructorReference extends SpelNodeImpl {
Object intendedArrayType = getChild(0).getValue(state);
if (!(intendedArrayType instanceof String)) {
throw new SpelEvaluationException(getChild(0).getStartPosition(),
SpelMessage.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION, FormatHelper
.formatClassNameForMessage(intendedArrayType.getClass()));
SpelMessage.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION,
FormatHelper.formatClassNameForMessage(intendedArrayType.getClass()));
}
String type = (String) intendedArrayType;
Class<?> componentType;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,8 +21,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents the elvis operator ?:. For an expression "a?:b" if a is not null, the value of the expression
* is "a", if a is null then the value of the expression is "b".
* Represents the elvis operator ?:. For an expression "a?:b" if a is not null, the value
* of the expression is "a", if a is null then the value of the expression is "b".
*
* @author Andy Clement
* @since 3.0
@ -33,25 +33,30 @@ public class Elvis extends SpelNodeImpl {
super(pos,args);
}
/**
* Evaluate the condition and if not null, return it. If it is null return the other value.
* Evaluate the condition and if not null, return it. If it is null return the other
* value.
* @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
* @throws EvaluationException if the condition does not evaluate correctly to a
* boolean or there is a problem executing the chosen alternative
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue value = children[0].getValueInternal(state);
if (value.getValue()!=null && !((value.getValue() instanceof String) && ((String)value.getValue()).length()==0)) {
TypedValue value = this.children[0].getValueInternal(state);
if ((value.getValue() != null) && !((value.getValue() instanceof String) &&
((String) value.getValue()).length() == 0)) {
return value;
} else {
return children[1].getValueInternal(state);
}
else {
return this.children[1].getValueInternal(state);
}
}
@Override
public String toStringAST() {
return new StringBuilder().append(getChild(0).toStringAST()).append(" ?: ").append(getChild(1).toStringAST()).toString();
return new StringBuilder().append(getChild(0).toStringAST()).append(" ?: ").append(
getChild(1).toStringAST()).toString();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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.springframework.expression.TypedValue;
@ -24,6 +25,7 @@ import org.springframework.expression.TypedValue;
* @since 3.2
*/
public class FloatLiteral extends Literal {
private final TypedValue value;
FloatLiteral(String payload, int pos, float value) {
@ -31,6 +33,7 @@ public class FloatLiteral extends Literal {
this.value = new TypedValue(value);
}
@Override
public TypedValue getLiteralValue() {
return this.value;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -75,7 +75,8 @@ public class FormatHelper {
for (int i = 0; i < dims; i++) {
fmtd.append("[]");
}
} else {
}
else {
fmtd.append(clazz.getName());
}
return fmtd.toString();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,14 +31,15 @@ import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.util.ReflectionUtils;
/**
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in the context prior to the
* expression being evaluated or within the expression itself using a lambda function definition. For example: Lambda
* function definition in an expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function:
* "#isEven(37)". Functions may also be static java methods, registered in the context prior to invocation of the
* expression.
* A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in
* the context prior to the expression being evaluated or within the expression itself
* using a lambda function definition. For example: Lambda function definition in an
* expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function:
* "#isEven(37)". Functions may also be static java methods, registered in the context
* prior to invocation of the expression.
*
* <p>Functions are very simplistic, the arguments are not part of the definition (right now),
* so the names must be unique.
* <p>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
@ -47,21 +48,23 @@ public class FunctionReference extends SpelNodeImpl {
private final String name;
public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
super(pos,arguments);
name = functionName;
this.name = functionName;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
TypedValue o = state.lookupVariable(name);
TypedValue o = state.lookupVariable(this.name);
if (o == null) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, name);
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
}
// Two possibilities: a lambda function or a Java static method registered as a function
if (!(o.getValue() instanceof Method)) {
throw new SpelEvaluationException(SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, name, o.getClass());
throw new SpelEvaluationException(SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, o.getClass());
}
try {
return executeFunctionJLRMethod(state, (Method) o.getValue());
@ -89,9 +92,9 @@ public class FunctionReference extends SpelNodeImpl {
}
// Only static methods can be called in this way
if (!Modifier.isStatic(method.getModifiers())) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_MUST_BE_STATIC, method
.getDeclaringClass().getName()
+ "." + method.getName(), name);
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.FUNCTION_MUST_BE_STATIC,
method.getDeclaringClass().getName() + "." + method.getName(), this.name);
}
// Convert arguments if necessary and remap them for varargs if required
@ -100,7 +103,8 @@ public class FunctionReference extends SpelNodeImpl {
ReflectionHelper.convertAllArguments(converter, functionArgs, method);
}
if (method.isVarArgs()) {
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs);
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
method.getParameterTypes(), functionArgs);
}
try {
@ -116,11 +120,12 @@ public class FunctionReference extends SpelNodeImpl {
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder("#").append(name);
StringBuilder sb = new StringBuilder("#").append(this.name);
sb.append("(");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
if (i > 0) {
sb.append(",");
}
sb.append(getChild(i).toStringAST());
}
sb.append(")");
@ -137,7 +142,7 @@ public class FunctionReference extends SpelNodeImpl {
// Compute arguments to the function
Object[] arguments = new Object[getChildCount()];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = children[i].getValueInternal(state).getValue();
arguments[i] = this.children[i].getValueInternal(state).getValue();
}
return arguments;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,14 +27,16 @@ public class Identifier extends SpelNodeImpl {
private final TypedValue id;
public Identifier(String payload,int pos) {
super(pos);
this.id = new TypedValue(payload);
}
@Override
public String toStringAST() {
return (String)this.id.getValue();
return (String) this.id.getValue();
}
@Override

View File

@ -33,9 +33,8 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
/**
* An Indexer can index into some proceeding structure to access a particular
* piece of it. Supported structures are: strings/collections
* (lists/sets)/arrays
* An Indexer can index into some proceeding structure to access a particular piece of it.
* Supported structures are: strings/collections (lists/sets)/arrays
*
* @author Andy Clement
* @author Phillip Webb
@ -95,26 +94,28 @@ public class Indexer extends SpelNodeImpl {
private final Object array;
private final int idx;
private final int index;
private final TypeDescriptor typeDescriptor;
ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int idx, TypeDescriptor typeDescriptor) {
ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int index, TypeDescriptor typeDescriptor) {
this.typeConverter = typeConverter;
this.array = array;
this.idx = idx;
this.index = index;
this.typeDescriptor = typeDescriptor;
}
@Override
public TypedValue getValue() {
Object arrayElement = accessArrayElement(this.array, this.idx);
Object arrayElement = accessArrayElement(this.array, this.index);
return new TypedValue(arrayElement, this.typeDescriptor.elementTypeDescriptor(arrayElement));
}
@Override
public void setValue(Object newValue) {
setArrayElement(this.typeConverter, this.array, this.idx, newValue,
setArrayElement(this.typeConverter, this.array, this.index, newValue,
this.typeDescriptor.getElementTypeDescriptor().getType());
}
@ -136,17 +137,21 @@ public class Indexer extends SpelNodeImpl {
private final TypeDescriptor mapEntryTypeDescriptor;
MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryTypeDescriptor) {
MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key,
TypeDescriptor mapEntryTypeDescriptor) {
this.typeConverter = typeConverter;
this.map = map;
this.key = key;
this.mapEntryTypeDescriptor = mapEntryTypeDescriptor;
}
@Override
public TypedValue getValue() {
Object value = this.map.get(this.key);
return new TypedValue(value, this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value));
return new TypedValue(value,
this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value));
}
@Override
@ -171,71 +176,75 @@ public class Indexer extends SpelNodeImpl {
private final String name;
private final EvaluationContext eContext;
private final EvaluationContext evaluationContext;
private final TypeDescriptor targetObjectTypeDescriptor;
private final TypeDescriptor td;
public PropertyIndexingValueRef(Object targetObject, String value, EvaluationContext evaluationContext,
TypeDescriptor targetObjectTypeDescriptor) {
this.targetObject = targetObject;
this.name = value;
this.eContext = evaluationContext;
this.td = targetObjectTypeDescriptor;
this.evaluationContext = evaluationContext;
this.targetObjectTypeDescriptor = targetObjectTypeDescriptor;
}
@Override
public TypedValue getValue() {
Class<?> targetObjectRuntimeClass = getObjectClass(targetObject);
Class<?> targetObjectRuntimeClass = getObjectClass(this.targetObject);
try {
if (cachedReadName != null && cachedReadName.equals(name) && cachedReadTargetType != null &&
cachedReadTargetType.equals(targetObjectRuntimeClass)) {
if (Indexer.this.cachedReadName != null && Indexer.this.cachedReadName.equals(this.name) && Indexer.this.cachedReadTargetType != null &&
Indexer.this.cachedReadTargetType.equals(targetObjectRuntimeClass)) {
// it is OK to use the cached accessor
return cachedReadAccessor.read(this.eContext, this.targetObject, this.name);
return Indexer.this.cachedReadAccessor.read(this.evaluationContext, this.targetObject, this.name);
}
List<PropertyAccessor> accessorsToTry =
AstUtils.getPropertyAccessorsToTry(targetObjectRuntimeClass, eContext.getPropertyAccessors());
List<PropertyAccessor> accessorsToTry = AstUtils.getPropertyAccessorsToTry(
targetObjectRuntimeClass, this.evaluationContext.getPropertyAccessors());
if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canRead(this.eContext, this.targetObject, this.name)) {
if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) {
if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
this.eContext, this.targetObject, this.name);
this.evaluationContext, this.targetObject, this.name);
}
cachedReadAccessor = accessor;
cachedReadName = this.name;
cachedReadTargetType = targetObjectRuntimeClass;
return accessor.read(this.eContext, this.targetObject, this.name);
Indexer.this.cachedReadAccessor = accessor;
Indexer.this.cachedReadName = this.name;
Indexer.this.cachedReadTargetType = targetObjectRuntimeClass;
return accessor.read(this.evaluationContext, this.targetObject, this.name);
}
}
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.td.toString());
this.targetObjectTypeDescriptor.toString());
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.td.toString());
this.targetObjectTypeDescriptor.toString());
}
@Override
public void setValue(Object newValue) {
Class<?> contextObjectClass = getObjectClass(targetObject);
Class<?> contextObjectClass = getObjectClass(this.targetObject);
try {
if (cachedWriteName != null && cachedWriteName.equals(name) && cachedWriteTargetType != null &&
cachedWriteTargetType.equals(contextObjectClass)) {
if (Indexer.this.cachedWriteName != null && Indexer.this.cachedWriteName.equals(this.name) && Indexer.this.cachedWriteTargetType != null &&
Indexer.this.cachedWriteTargetType.equals(contextObjectClass)) {
// it is OK to use the cached accessor
cachedWriteAccessor.write(this.eContext, this.targetObject, this.name, newValue);
Indexer.this.cachedWriteAccessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
return;
}
List<PropertyAccessor> accessorsToTry =
AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.eContext.getPropertyAccessors());
AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.evaluationContext.getPropertyAccessors());
if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) {
if (accessor.canWrite(this.eContext, this.targetObject, this.name)) {
cachedWriteName = this.name;
cachedWriteTargetType = contextObjectClass;
cachedWriteAccessor = accessor;
accessor.write(this.eContext, this.targetObject, this.name, newValue);
if (accessor.canWrite(this.evaluationContext, this.targetObject, this.name)) {
Indexer.this.cachedWriteName = this.name;
Indexer.this.cachedWriteTargetType = contextObjectClass;
Indexer.this.cachedWriteAccessor = accessor;
accessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
return;
}
}
@ -267,7 +276,8 @@ public class Indexer extends SpelNodeImpl {
private final boolean growCollection;
private int maximumSize;
private final int maximumSize;
CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor,
TypeConverter typeConverter, boolean growCollection, int maximumSize) {
@ -279,6 +289,7 @@ public class Indexer extends SpelNodeImpl {
this.maximumSize = maximumSize;
}
@Override
public TypedValue getValue() {
growCollectionIfNecessary();
@ -356,19 +367,21 @@ public class Indexer extends SpelNodeImpl {
private final int index;
private final TypeDescriptor td;
private final TypeDescriptor typeDescriptor;
public StringIndexingLValue(String target, int index, TypeDescriptor td) {
public StringIndexingLValue(String target, int index, TypeDescriptor typeDescriptor) {
this.target = target;
this.index = index;
this.td = td;
this.typeDescriptor = typeDescriptor;
}
@Override
public TypedValue getValue() {
if (this.index >= this.target.length()) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.STRING_INDEX_OUT_OF_BOUNDS,
this.target.length(), index);
this.target.length(), this.index);
}
return new TypedValue(String.valueOf(this.target.charAt(this.index)));
}
@ -376,7 +389,7 @@ public class Indexer extends SpelNodeImpl {
@Override
public void setValue(Object newValue) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
this.td.toString());
this.typeDescriptor.toString());
}
@Override
@ -387,6 +400,7 @@ public class Indexer extends SpelNodeImpl {
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue();
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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;
@ -35,11 +36,13 @@ public class InlineList extends SpelNodeImpl {
// if the list is purely literals, it is a constant value and can be computed and cached
TypedValue constant = null; // TODO must be immutable list
public InlineList(int pos, SpelNodeImpl... args) {
super(pos, args);
checkIfConstant();
}
/**
* If all the components of the list are constants, or lists that themselves contain constants, then a constant list
* can be built to represent this node. This will speed up later getValue calls and reduce the amount of garbage
@ -55,7 +58,8 @@ public class InlineList extends SpelNodeImpl {
if (!inlineList.isConstant()) {
isConstant = false;
}
} else {
}
else {
isConstant = false;
}
}
@ -67,7 +71,8 @@ public class InlineList extends SpelNodeImpl {
SpelNode child = getChild(c);
if ((child instanceof Literal)) {
constantList.add(((Literal) child).getLiteralValue().getValue());
} else if (child instanceof InlineList) {
}
else if (child instanceof InlineList) {
constantList.add(((InlineList) child).getConstantValue());
}
}
@ -77,9 +82,10 @@ public class InlineList extends SpelNodeImpl {
@Override
public TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException {
if (constant != null) {
return constant;
} else {
if (this.constant != null) {
return this.constant;
}
else {
List<Object> returnValue = new ArrayList<Object>();
int childcount = getChildCount();
for (int c = 0; c < childcount; c++) {
@ -109,12 +115,12 @@ public class InlineList extends SpelNodeImpl {
* @return whether this list is a constant value
*/
public boolean isConstant() {
return constant != null;
return this.constant != null;
}
@SuppressWarnings("unchecked")
private List<Object> getConstantValue() {
return (List<Object>) constant.getValue();
return (List<Object>) this.constant.getValue();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,11 +27,13 @@ public class IntLiteral extends Literal {
private final TypedValue value;
IntLiteral(String payload, int pos, int value) {
super(payload, pos);
this.value = new TypedValue(value);
}
@Override
public TypedValue getLiteralValue() {
return this.value;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,11 +28,13 @@ public class LongLiteral extends Literal {
private final TypedValue value;
LongLiteral(String payload, int pos, long value) {
super(payload, pos);
this.value = new TypedValue(value);
}
@Override
public TypedValue getLiteralValue() {
return this.value;

View File

@ -33,6 +33,8 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
/**
* Expression language AST node that represents a method reference.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
@ -112,8 +114,8 @@ public class MethodReference extends SpelNodeImpl {
MethodExecutor executorToUse = this.cachedExecutor;
if (executorToUse != null) {
try {
return executorToUse.execute(
state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
return executorToUse.execute(state.getEvaluationContext(),
state.getActiveContextObject().getValue(), arguments);
}
catch (AccessException ae) {
// Two reasons this can occur:
@ -137,8 +139,8 @@ public class MethodReference extends SpelNodeImpl {
executorToUse = findAccessorForMethod(this.name, getTypes(arguments), state);
this.cachedExecutor = executorToUse;
try {
return executorToUse.execute(
state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
return executorToUse.execute(state.getEvaluationContext(),
state.getActiveContextObject().getValue(), arguments);
}
catch (AccessException ae) {
// Same unwrapping exception handling as above in above catch block
@ -158,12 +160,10 @@ public class MethodReference extends SpelNodeImpl {
if (rootCause instanceof RuntimeException) {
throw (RuntimeException) rootCause;
}
else {
throw new ExpressionInvocationTargetException(getStartPosition(),
"A problem occurred when trying to execute method '" + this.name +
"' on object of type '" + state.getActiveContextObject().getValue().getClass().getName() + "'",
rootCause);
}
throw new ExpressionInvocationTargetException(getStartPosition(),
"A problem occurred when trying to execute method '" + this.name +
"' on object of type '" + state.getActiveContextObject().getValue().getClass().getName() + "'",
rootCause);
}
}
@ -180,42 +180,49 @@ public class MethodReference extends SpelNodeImpl {
StringBuilder sb = new StringBuilder();
sb.append(this.name).append("(");
for (int i = 0; i < getChildCount(); i++) {
if (i > 0)
if (i > 0) {
sb.append(",");
}
sb.append(getChild(i).toStringAST());
}
sb.append(")");
return sb.toString();
}
private MethodExecutor findAccessorForMethod(String name, List<TypeDescriptor> argumentTypes, ExpressionState state)
private MethodExecutor findAccessorForMethod(String name,
List<TypeDescriptor> argumentTypes, ExpressionState state)
throws SpelEvaluationException {
return findAccessorForMethod(name,argumentTypes,state.getActiveContextObject().getValue(),state.getEvaluationContext());
return findAccessorForMethod(name, argumentTypes,
state.getActiveContextObject().getValue(), state.getEvaluationContext());
}
private MethodExecutor findAccessorForMethod(String name,
List<TypeDescriptor> argumentTypes, Object contextObject, EvaluationContext eContext)
throws SpelEvaluationException {
List<MethodResolver> mResolvers = eContext.getMethodResolvers();
if (mResolvers != null) {
for (MethodResolver methodResolver : mResolvers) {
List<MethodResolver> methodResolvers = eContext.getMethodResolvers();
if (methodResolvers != null) {
for (MethodResolver methodResolver : methodResolvers) {
try {
MethodExecutor cEx = methodResolver.resolve(eContext, contextObject, name, argumentTypes);
if (cEx != null) {
return cEx;
MethodExecutor methodExecutor = methodResolver.resolve(eContext,
contextObject, name, argumentTypes);
if (methodExecutor != null) {
return methodExecutor;
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(),ex, SpelMessage.PROBLEM_LOCATING_METHOD,
name, contextObject.getClass());
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.PROBLEM_LOCATING_METHOD, name,
contextObject.getClass());
}
}
}
throw new SpelEvaluationException(getStartPosition(),SpelMessage.METHOD_NOT_FOUND,
throw new SpelEvaluationException(
getStartPosition(),
SpelMessage.METHOD_NOT_FOUND,
FormatHelper.formatMethodForMessage(name, argumentTypes),
FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class<?>) contextObject) : contextObject.getClass()));
FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class<?>) contextObject)
: contextObject.getClass()));
}
@ -229,6 +236,7 @@ public class MethodReference extends SpelNodeImpl {
private final Object[] arguments;
MethodValueRef(ExpressionState state, EvaluationContext evaluationContext, Object object, Object[] arguments) {
this.state = state;
this.evaluationContext = evaluationContext;
@ -236,9 +244,10 @@ public class MethodReference extends SpelNodeImpl {
this.arguments = arguments;
}
@Override
public TypedValue getValue() {
MethodExecutor executorToUse = cachedExecutor;
MethodExecutor executorToUse = MethodReference.this.cachedExecutor;
if (executorToUse != null) {
try {
return executorToUse.execute(this.evaluationContext, this.target, this.arguments);
@ -257,21 +266,23 @@ public class MethodReference extends SpelNodeImpl {
throwSimpleExceptionIfPossible(this.state, ae);
// at this point we know it wasn't a user problem so worth a retry if a better candidate can be found
cachedExecutor = null;
MethodReference.this.cachedExecutor = null;
}
}
// either there was no accessor or it no longer existed
executorToUse = findAccessorForMethod(name, getTypes(this.arguments), this.target, this.evaluationContext);
cachedExecutor = executorToUse;
executorToUse = findAccessorForMethod(MethodReference.this.name, getTypes(this.arguments), this.target, this.evaluationContext);
MethodReference.this.cachedExecutor = executorToUse;
try {
return executorToUse.execute(this.evaluationContext, this.target, this.arguments);
}
catch (AccessException ae) {
catch (AccessException ex) {
// Same unwrapping exception handling as above in above catch block
throwSimpleExceptionIfPossible(this.state, ae);
throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION,
name, this.state.getActiveContextObject().getValue().getClass().getName(), ae.getMessage());
throwSimpleExceptionIfPossible(this.state, ex);
throw new SpelEvaluationException(getStartPosition(), ex,
SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION,
MethodReference.this.name, this.state.getActiveContextObject().getValue().getClass().getName(),
ex.getMessage());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
/**
* Expression language AST node that represents null.
*
* @author Andy Clement
* @since 3.0
*/
@ -28,6 +30,7 @@ public class NullLiteral extends Literal {
super(null,pos);
}
@Override
public TypedValue getLiteralValue() {
return TypedValue.NULL;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,6 +37,7 @@ public class OpAnd extends Operator {
super("and", pos, operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
if (getBooleanValue(state, getLeftOperand()) == false) {
@ -52,9 +53,9 @@ public class OpAnd extends Operator {
assertValueNotNull(value);
return value;
}
catch (SpelEvaluationException ee) {
ee.setPosition(operand.getStartPosition());
throw ee;
catch (SpelEvaluationException ex) {
ex.setPosition(operand.getStartPosition());
throw ex;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,7 +33,8 @@ import org.springframework.util.Assert;
*/
public class OpDec extends Operator {
private boolean postfix; // false means prefix
private final boolean postfix; // false means prefix
public OpDec(int pos, boolean postfix, SpelNodeImpl... operands) {
super("--", pos, operands);
@ -41,6 +42,7 @@ public class OpDec extends Operator {
this.postfix = postfix;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl operand = getLeftOperand();
@ -57,26 +59,37 @@ public class OpDec extends Operator {
if (operandValue instanceof Number) {
Number op1 = (Number) operandValue;
if (op1 instanceof Double) {
newValue = new TypedValue(op1.doubleValue() - 1.0d, operandTypedValue.getTypeDescriptor());
} else if (op1 instanceof Float) {
newValue = new TypedValue(op1.floatValue() - 1.0f, operandTypedValue.getTypeDescriptor());
} else if (op1 instanceof Long) {
newValue = new TypedValue(op1.longValue() - 1L, operandTypedValue.getTypeDescriptor());
} else if (op1 instanceof Short) {
newValue = new TypedValue(op1.shortValue() - (short)1, operandTypedValue.getTypeDescriptor());
} else {
newValue = new TypedValue(op1.intValue() - 1, operandTypedValue.getTypeDescriptor());
newValue = new TypedValue(op1.doubleValue() - 1.0d,
operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Float) {
newValue = new TypedValue(op1.floatValue() - 1.0f,
operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Long) {
newValue = new TypedValue(op1.longValue() - 1L,
operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Short) {
newValue = new TypedValue(op1.shortValue() - (short) 1,
operandTypedValue.getTypeDescriptor());
}
else {
newValue = new TypedValue(op1.intValue() - 1,
operandTypedValue.getTypeDescriptor());
}
}
if (newValue==null) {
try {
newValue = state.operate(Operation.SUBTRACT, returnValue.getValue(), 1);
} catch (SpelEvaluationException see) {
if (see.getMessageCode()==SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
}
catch (SpelEvaluationException ex) {
if (ex.getMessageCode() == SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
// This means the operand is not decrementable
throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_DECREMENTABLE,operand.toStringAST());
} else {
throw see;
}
else {
throw ex;
}
}
}
@ -84,16 +97,19 @@ public class OpDec extends Operator {
// set the new value
try {
lvalue.setValue(newValue.getValue());
} catch (SpelEvaluationException see) {
}
catch (SpelEvaluationException see) {
// if unable to set the value the operand is not writable (e.g. 1-- )
if (see.getMessageCode()==SpelMessage.SETVALUE_NOT_SUPPORTED) {
throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_DECREMENTABLE);
} else {
if (see.getMessageCode() == SpelMessage.SETVALUE_NOT_SUPPORTED) {
throw new SpelEvaluationException(operand.getStartPosition(),
SpelMessage.OPERAND_NOT_DECREMENTABLE);
}
else {
throw see;
}
}
if (!postfix) {
if (!this.postfix) {
// the return value is the new value, not the original value
returnValue = newValue;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -34,6 +34,7 @@ public class OpDivide extends Operator {
super("/", pos, operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
@ -43,9 +44,11 @@ public class OpDivide extends Operator {
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() / op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() / op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() / op2.longValue());
}
else {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,8 +32,10 @@ public class OpEQ extends Operator {
super("==", pos, operands);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
public BooleanTypedValue getValueInternal(ExpressionState state)
throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
@ -41,18 +43,23 @@ public class OpEQ extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return BooleanTypedValue.forValue(op1.doubleValue() == op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return BooleanTypedValue.forValue(op1.floatValue() == op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return BooleanTypedValue.forValue(op1.floatValue() == op2.floatValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() == op2.longValue());
} else {
}
else {
return BooleanTypedValue.forValue(op1.intValue() == op2.intValue());
}
}
if (left!=null && (left instanceof Comparable)) {
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) == 0);
} else {
return BooleanTypedValue.forValue(left==right);
if (left != null && (left instanceof Comparable)) {
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left,
right) == 0);
}
else {
return BooleanTypedValue.forValue(left == right);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,6 +31,7 @@ public class OpGE extends Operator {
super(">=", pos, operands);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
@ -40,11 +41,14 @@ public class OpGE extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue());
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() >= rightNumber.floatValue());
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue( leftNumber.longValue() >= rightNumber.longValue());
} else {
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() >= rightNumber.longValue());
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,6 +32,7 @@ public class OpGT extends Operator {
super(">", pos, operands);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
@ -41,9 +42,11 @@ public class OpGT extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue());
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() > rightNumber.longValue());
} else {
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,7 +33,8 @@ import org.springframework.util.Assert;
*/
public class OpInc extends Operator {
private boolean postfix; // false means prefix
private final boolean postfix; // false means prefix
public OpInc(int pos, boolean postfix, SpelNodeImpl... operands) {
super("++", pos, operands);
@ -41,6 +42,7 @@ public class OpInc extends Operator {
this.postfix = postfix;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl operand = getLeftOperand();
@ -55,43 +57,56 @@ public class OpInc extends Operator {
if (operandValue instanceof Number) {
Number op1 = (Number) operandValue;
if (op1 instanceof Double) {
newValue = new TypedValue(op1.doubleValue() + 1.0d, operandTypedValue.getTypeDescriptor());
} else if (op1 instanceof Float) {
newValue = new TypedValue(op1.floatValue() + 1.0f, operandTypedValue.getTypeDescriptor());
} else if (op1 instanceof Long) {
newValue = new TypedValue(op1.longValue() + 1L, operandTypedValue.getTypeDescriptor());
} else if (op1 instanceof Short) {
newValue = new TypedValue(op1.shortValue() + (short)1, operandTypedValue.getTypeDescriptor());
} else {
newValue = new TypedValue(op1.intValue() + 1, operandTypedValue.getTypeDescriptor());
newValue = new TypedValue(op1.doubleValue() + 1.0d,
operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Float) {
newValue = new TypedValue(op1.floatValue() + 1.0f,
operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Long) {
newValue = new TypedValue(op1.longValue() + 1L,
operandTypedValue.getTypeDescriptor());
}
else if (op1 instanceof Short) {
newValue = new TypedValue(op1.shortValue() + (short) 1,
operandTypedValue.getTypeDescriptor());
}
else {
newValue = new TypedValue(op1.intValue() + 1,
operandTypedValue.getTypeDescriptor());
}
}
if (newValue==null) {
if (newValue == null) {
try {
newValue = state.operate(Operation.ADD, returnValue.getValue(), 1);
} catch (SpelEvaluationException see) {
if (see.getMessageCode()==SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
}
catch (SpelEvaluationException ex) {
if (ex.getMessageCode() == SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
// This means the operand is not incrementable
throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_INCREMENTABLE,operand.toStringAST());
} else {
throw see;
throw new SpelEvaluationException(operand.getStartPosition(),
SpelMessage.OPERAND_NOT_INCREMENTABLE, operand.toStringAST());
}
throw ex;
}
}
// set the name value
try {
lvalue.setValue(newValue.getValue());
} catch (SpelEvaluationException see) {
}
catch (SpelEvaluationException see) {
// if unable to set the value the operand is not writable (e.g. 1++ )
if (see.getMessageCode()==SpelMessage.SETVALUE_NOT_SUPPORTED) {
throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_INCREMENTABLE);
} else {
if (see.getMessageCode() == SpelMessage.SETVALUE_NOT_SUPPORTED) {
throw new SpelEvaluationException(operand.getStartPosition(),
SpelMessage.OPERAND_NOT_INCREMENTABLE);
}
else {
throw see;
}
}
if (!postfix) {
if (!this.postfix) {
// the return value is the new value, not the original value
returnValue = newValue;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,8 +32,10 @@ public class OpLE extends Operator {
super("<=", pos, operands);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
public BooleanTypedValue getValueInternal(ExpressionState state)
throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
@ -41,15 +43,18 @@ public class OpLE extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue());
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() <= rightNumber.floatValue());
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() <= rightNumber.longValue());
} else {
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue());
}
}
return BooleanTypedValue.forValue( state.getTypeComparator().compare(left, right) <= 0);
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) <= 0);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,8 +32,10 @@ public class OpLT extends Operator {
super("<", pos, operands);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
public BooleanTypedValue getValueInternal(ExpressionState state)
throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
// TODO could leave all of these to the comparator - just seems quicker to do some here
@ -42,11 +44,14 @@ public class OpLT extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue());
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() < rightNumber.floatValue());
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() < rightNumber.longValue());
} else {
}
else {
return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,11 +27,12 @@ import org.springframework.expression.spel.ExpressionState;
* <li>subtraction of doubles (floats are represented as doubles)
* <li>subtraction of longs
* <li>subtraction of integers
* <li>subtraction of an int from a string of one character (effectively decreasing that character), so 'd'-3='a'
* <li>subtraction of an int from a string of one character (effectively decreasing that
* character), so 'd'-3='a'
* </ul>
* It can be used as a unary operator for numbers (double/long/int). The standard promotions are performed
* when the operand types vary (double-int=double).
* For other options it defers to the registered overloader.
* It can be used as a unary operator for numbers (double/long/int). The standard
* promotions are performed when the operand types vary (double-int=double). For other
* options it defers to the registered overloader.
*
* @author Andy Clement
* @since 3.0
@ -44,46 +45,61 @@ public class OpMinus extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) {// If only one operand, then this is unary minus
Object operand = leftOp.getValueInternal(state).getValue();
if (operand instanceof Number) {
Number n = (Number) operand;
if (operand instanceof Double) {
return new TypedValue(0 - n.doubleValue());
} else if (operand instanceof Float) {
}
if (operand instanceof Float) {
return new TypedValue(0 - n.floatValue());
} else if (operand instanceof Long) {
}
if (operand instanceof Long) {
return new TypedValue(0 - n.longValue());
} else {
return new TypedValue(0 - n.intValue());
}
return new TypedValue(0 - n.intValue());
}
return state.operate(Operation.SUBTRACT, operand, null);
} else {
Object left = leftOp.getValueInternal(state).getValue();
Object right = rightOp.getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() - op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() - op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() - op2.longValue());
} else {
return new TypedValue(op1.intValue() - op2.intValue());
}
} else if (left instanceof String && right instanceof Integer && ((String)left).length()==1) {
String theString = (String) left;
Integer theInteger = (Integer) right;
// implements character - int (ie. b - 1 = a)
return new TypedValue(Character.toString((char) (theString.charAt(0) - theInteger)));
}
return state.operate(Operation.SUBTRACT, left, right);
}
Object left = leftOp.getValueInternal(state).getValue();
Object right = rightOp.getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() - op2.doubleValue());
}
if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() - op2.floatValue());
}
if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() - op2.longValue());
}
return new TypedValue(op1.intValue() - op2.intValue());
}
else if (left instanceof String && right instanceof Integer
&& ((String) left).length() == 1) {
String theString = (String) left;
Integer theInteger = (Integer) right;
// implements character - int (ie. b - 1 = a)
return new TypedValue(Character.toString((char)
(theString.charAt(0) - theInteger)));
}
return state.operate(Operation.SUBTRACT, left, right);
}
@Override
@ -95,8 +111,8 @@ public class OpMinus extends Operator {
}
@Override
public SpelNodeImpl getRightOperand() {
if (children.length<2) {return null;}
return children[1];
if (this.children.length<2) {return null;}
return this.children[1];
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,6 +33,7 @@ public class OpModulus extends Operator {
super("%", pos, operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
@ -42,11 +43,14 @@ public class OpModulus extends Operator {
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() % op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
}
else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() % op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
}
else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() % op2.longValue());
} else {
}
else {
return new TypedValue(op1.intValue() % op2.intValue());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,10 +25,11 @@ import org.springframework.expression.spel.ExpressionState;
* Implements the {@code multiply} operator.
*
* <p>Conversions and promotions are handled as defined in
* <a href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html">Section 5.6.2
* of the Java Language Specification</a>:
* <a href="http://java.sun.com/docs/books/jls/third_edition/html/conversions.html">Section
* 5.6.2 of the Java Language Specification</a>:
*
* <p>If any of the operands is of a reference type, unboxing conversion (Section 5.1.8) is performed. Then:<br>
* <p>If any of the operands is of a reference type, unboxing conversion (Section 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>
@ -44,6 +45,7 @@ public class OpMultiply extends Operator {
super("*", pos, operands);
}
/**
* Implements the {@code multiply} operator directly here for certain types
* of supported operands and otherwise delegates to any registered overloader
@ -58,23 +60,27 @@ public class OpMultiply extends Operator {
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().getValueInternal(state).getValue();
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number leftNumber = (Number) operandOne;
Number rightNumber = (Number) operandTwo;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
return new TypedValue(leftNumber.doubleValue()
* rightNumber.doubleValue());
}
else if (leftNumber instanceof Float || rightNumber instanceof Float) {
if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue());
}
else if (leftNumber instanceof Long || rightNumber instanceof Long) {
if (leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue(leftNumber.longValue() * rightNumber.longValue());
}
else {
return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
}
return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
}
else if (operandOne instanceof String && operandTwo instanceof Integer) {
int repeats = (Integer) operandTwo;
@ -84,6 +90,7 @@ public class OpMultiply extends Operator {
}
return new TypedValue(result.toString());
}
return state.operate(Operation.MULTIPLY, operandOne, operandTwo);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,29 +32,38 @@ public class OpNE extends Operator {
super("!=", pos, operands);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return BooleanTypedValue.forValue(op1.doubleValue() != op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return BooleanTypedValue.forValue(op1.floatValue() != op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() != op2.longValue());
} else {
return BooleanTypedValue.forValue(op1.intValue() != op2.intValue());
}
if (op1 instanceof Float || op2 instanceof Float) {
return BooleanTypedValue.forValue(op1.floatValue() != op2.floatValue());
}
if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() != op2.longValue());
}
return BooleanTypedValue.forValue(op1.intValue() != op2.intValue());
}
if (left!=null && (left instanceof Comparable)) {
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) != 0);
} else {
return BooleanTypedValue.forValue(left!=right);
if (left != null && (left instanceof Comparable)) {
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left,
right) != 0);
}
return BooleanTypedValue.forValue(left != right);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,6 +36,7 @@ public class OpOr extends Operator {
super("or", pos, operands);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
if (getBooleanValue(state, getLeftOperand())) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,8 +32,9 @@ import org.springframework.util.Assert;
* <li>add integers
* <li>concatenate strings
* </ul>
* It can be used as a unary operator for numbers (double/long/int). The standard promotions are performed
* when the operand types vary (double+int=double). For other options it defers to the registered overloader.
* It can be used as a unary operator for numbers (double/long/int). The standard
* promotions are performed when the operand types vary (double+int=double). For other
* options it defers to the registered overloader.
*
* @author Andy Clement
* @author Ivo Smid
@ -46,72 +47,86 @@ public class OpPlus extends Operator {
Assert.notEmpty(operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
if (rightOp == null) { // If only one operand, then this is unary plus
Object operandOne = leftOp.getValueInternal(state).getValue();
if (operandOne instanceof Number) {
if (operandOne instanceof Double || operandOne instanceof Long) {
return new TypedValue(operandOne);
} else if (operandOne instanceof Float) {
return new TypedValue(((Number) operandOne).floatValue());
} else {
return new TypedValue(((Number) operandOne).intValue());
}
if (operandOne instanceof Float) {
return new TypedValue(((Number) operandOne).floatValue());
}
return new TypedValue(((Number) operandOne).intValue());
}
return state.operate(Operation.ADD, operandOne, null);
}
else {
final TypedValue operandOneValue = leftOp.getValueInternal(state);
final Object operandOne = operandOneValue.getValue();
final TypedValue operandTwoValue = rightOp.getValueInternal(state);
final Object operandTwo = operandTwoValue.getValue();
final TypedValue operandOneValue = leftOp.getValueInternal(state);
final Object operandOne = operandOneValue.getValue();
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() + op2.doubleValue());
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() + op2.floatValue());
} else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() + op2.longValue());
} else { // TODO what about overflow?
return new TypedValue(op1.intValue() + op2.intValue());
}
} else if (operandOne instanceof String && operandTwo instanceof String) {
return new TypedValue(new StringBuilder((String) operandOne).append((String) operandTwo).toString());
} else if (operandOne instanceof String) {
StringBuilder result = new StringBuilder((String) operandOne);
result.append((operandTwo == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
return new TypedValue(result.toString());
} else if (operandTwo instanceof String) {
StringBuilder result = new StringBuilder((operandOne == null ? "null" : convertTypedValueToString(
operandOneValue, state)));
result.append((String) operandTwo);
return new TypedValue(result.toString());
final TypedValue operandTwoValue = rightOp.getValueInternal(state);
final Object operandTwo = operandTwoValue.getValue();
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() + op2.doubleValue());
}
return state.operate(Operation.ADD, operandOne, operandTwo);
if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() + op2.floatValue());
}
if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() + op2.longValue());
}
// TODO what about overflow?
return new TypedValue(op1.intValue() + op2.intValue());
}
if (operandOne instanceof String && operandTwo instanceof String) {
return new TypedValue(new StringBuilder((String) operandOne).append(
(String) operandTwo).toString());
}
if (operandOne instanceof String) {
StringBuilder result = new StringBuilder((String) operandOne);
result.append((operandTwo == null ? "null" : convertTypedValueToString(
operandTwoValue, state)));
return new TypedValue(result.toString());
}
if (operandTwo instanceof String) {
StringBuilder result = new StringBuilder((operandOne == null ? "null"
: convertTypedValueToString(operandOneValue, state)));
result.append((String) operandTwo);
return new TypedValue(result.toString());
}
return state.operate(Operation.ADD, operandOne, operandTwo);
}
@Override
public String toStringAST() {
if (children.length<2) { // unary plus
if (this.children.length<2) { // unary plus
return new StringBuilder().append("+").append(getLeftOperand().toStringAST()).toString();
}
return super.toStringAST();
}
@Override
public SpelNodeImpl getRightOperand() {
if (children.length < 2) {
if (this.children.length < 2) {
return null;
}
return children[1];
return this.children[1];
}
/**
@ -127,11 +142,12 @@ public class OpPlus extends Operator {
final TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class);
if (typeConverter.canConvert(value.getTypeDescriptor(), typeDescriptor)) {
final Object obj = typeConverter.convertValue(value.getValue(), value.getTypeDescriptor(), typeDescriptor);
final Object obj = typeConverter.convertValue(value.getValue(),
value.getTypeDescriptor(), typeDescriptor);
return String.valueOf(obj);
} else {
return String.valueOf(value.getValue());
}
return String.valueOf(value.getValue());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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,10 +16,10 @@
package org.springframework.expression.spel.ast;
/**
* 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.
* 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
@ -28,21 +28,23 @@ public abstract class Operator extends SpelNodeImpl {
String operatorName;
public Operator(String payload,int pos,SpelNodeImpl... operands) {
super(pos, operands);
this.operatorName = payload;
}
public SpelNodeImpl getLeftOperand() {
return children[0];
return this.children[0];
}
public SpelNodeImpl getRightOperand() {
return children[1];
return this.children[1];
}
public final String getOperatorName() {
return operatorName;
return this.operatorName;
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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,9 +26,10 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* Represents the between operator. The left operand to between must be a single value and the right operand must be a
* list - this operator returns true if the left operand is between (using the registered comparator) the two elements
* in the list. The definition of between being inclusive follows the SQL BETWEEN definition.
* Represents the between operator. The left operand to between must be a single value and
* the right operand must be a list - this operator returns true if the left operand is
* between (using the registered comparator) the two elements in the list. The definition
* of between being inclusive follows the SQL BETWEEN definition.
*
* @author Andy Clement
* @since 3.0
@ -40,8 +41,9 @@ 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).
* 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
@ -59,8 +61,10 @@ public class OperatorBetween extends Operator {
Object high = l.get(1);
TypeComparator comparator = state.getTypeComparator();
try {
return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 && comparator.compare(left, high) <= 0));
} catch (SpelEvaluationException ex) {
return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 &&
comparator.compare(left, high) <= 0));
}
catch (SpelEvaluationException ex) {
ex.setPosition(getStartPosition());
throw ex;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,8 +24,8 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* The operator 'instanceof' checks if an object is of the class specified in the right hand operand,
* in the same way that {@code 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} does in Java.
*
* @author Andy Clement
* @since 3.0
@ -36,11 +36,13 @@ public class OperatorInstanceof extends Operator {
super("instanceof", pos, operands);
}
/**
* 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
* @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
*/
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,8 +27,9 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
* 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.
* 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
@ -39,11 +40,14 @@ public class OperatorMatches extends Operator {
super("matches", pos, operands);
}
/**
* 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)
* @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)
*/
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,18 +36,19 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
super(pos, operand);
}
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
try {
Boolean value = children[0].getValue(state, Boolean.class);
Boolean value = this.children[0].getValue(state, Boolean.class);
if (value == null) {
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
}
return BooleanTypedValue.forValue(!value);
}
catch (SpelEvaluationException see) {
see.setPosition(getChild(0).getStartPosition());
throw see;
catch (SpelEvaluationException ex) {
ex.setPosition(getChild(0).getStartPosition());
throw ex;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,6 +33,7 @@ public class OperatorPower extends Operator {
super("^", pos, operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
@ -44,18 +45,22 @@ public class OperatorPower extends Operator {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(Math.pow(op1.doubleValue(),op2.doubleValue()));
} else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(Math.pow(op1.doubleValue(), op2.doubleValue()));
}
else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(Math.pow(op1.floatValue(), op2.floatValue()));
} else if (op1 instanceof Long || op2 instanceof Long) {
double d= Math.pow(op1.longValue(), op2.longValue());
return new TypedValue((long)d);
} else {
double d= Math.pow(op1.longValue(), op2.longValue());
}
else if (op1 instanceof Long || op2 instanceof Long) {
double d = Math.pow(op1.longValue(), op2.longValue());
return new TypedValue((long) d);
}
else {
double d = Math.pow(op1.longValue(), op2.longValue());
if (d > Integer.MAX_VALUE) {
return new TypedValue((long)d);
} else {
return new TypedValue((int)d);
return new TypedValue((long) d);
}
else {
return new TypedValue((int) d);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,8 +32,8 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Represents projection, where a given operation is performed on all elements in some input sequence, returning
* a new sequence of the same size. For example:
* Represents projection, where a given operation is performed on all elements in some
* input sequence, returning a new sequence of the same size. For example:
* "{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}" returns "[n, y, n, y, n, y, n, y, n, y]"
*
* @author Andy Clement
@ -49,6 +49,7 @@ public class Projection extends SpelNodeImpl {
this.nullSafe = nullSafe;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
return getValueRef(state).getValue();
@ -81,7 +82,8 @@ public class Projection extends SpelNodeImpl {
}
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this); // TODO unable to build correct type descriptor
}
else if (operand instanceof Collection || operandIsArray) {
if (operand instanceof Collection || operandIsArray) {
Collection<?> data = (operand instanceof Collection ? (Collection<?>) operand :
Arrays.asList(ObjectUtils.toObjectArray(operand)));
List<Object> result = new ArrayList<Object>();
@ -91,7 +93,7 @@ public class Projection extends SpelNodeImpl {
try {
state.pushActiveContextObject(new TypedValue(element));
state.enterScope("index", idx);
Object value = children[0].getValueInternal(state).getValue();
Object value = this.children[0].getValueInternal(state).getValue();
if (value != null && operandIsArray) {
arrayElementType = determineCommonType(arrayElementType, value.getClass());
}
@ -113,21 +115,17 @@ public class Projection extends SpelNodeImpl {
}
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
}
else {
if (operand==null) {
if (this.nullSafe) {
return ValueRef.NullValueRef.instance;
}
else {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null");
}
}
else {
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
if (operand==null) {
if (this.nullSafe) {
return ValueRef.NullValueRef.instance;
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null");
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -68,10 +68,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
static class AccessorLValue implements ValueRef {
private PropertyOrFieldReference ref;
private TypedValue contextObject;
private EvaluationContext eContext;
private boolean isAutoGrowNullReferences;
private final PropertyOrFieldReference ref;
private final TypedValue contextObject;
private final EvaluationContext eContext;
private final boolean isAutoGrowNullReferences;
public AccessorLValue(
PropertyOrFieldReference propertyOrFieldReference,
@ -85,12 +85,12 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
@Override
public TypedValue getValue() {
return ref.getValueInternal(contextObject,eContext,isAutoGrowNullReferences);
return this.ref.getValueInternal(this.contextObject,this.eContext,this.isAutoGrowNullReferences);
}
@Override
public void setValue(Object newValue) {
ref.writeProperty(contextObject,eContext, ref.name, newValue);
this.ref.writeProperty(this.contextObject,this.eContext, this.ref.name, newValue);
}
@Override
@ -142,7 +142,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
try {
if (isWritableProperty(this.name,contextObject,eContext)) {
Map<?,?> newMap = HashMap.class.newInstance();
writeProperty(contextObject, eContext, name, newMap);
writeProperty(contextObject, eContext, this.name, newMap);
result = readProperty(contextObject, eContext, this.name);
}
}
@ -161,7 +161,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
try {
if (isWritableProperty(this.name,contextObject,eContext)) {
Object newObject = result.getTypeDescriptor().getType().newInstance();
writeProperty(contextObject, eContext, name, newObject);
writeProperty(contextObject, eContext, this.name, newObject);
result = readProperty(contextObject, eContext, this.name);
}
}
@ -253,7 +253,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue) throws SpelEvaluationException {
if (contextObject.getValue() == null && nullSafe) {
if (contextObject.getValue() == null && this.nullSafe) {
return;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
* Represents a dot separated sequence of strings that indicate a package qualified type reference.
* 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')"
*
@ -33,17 +34,19 @@ public class QualifiedIdentifier extends SpelNodeImpl {
// TODO safe to cache? dont think so
private TypedValue value;
public QualifiedIdentifier(int pos,SpelNodeImpl... operands) {
super(pos,operands);
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// Cache the concatenation of child identifiers
if (this.value == null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
Object value = children[i].getValueInternal(state).getValue();
Object value = this.children[i].getValueInternal(state).getValue();
if (i > 0 && !value.toString().startsWith("$")) {
sb.append(".");
}
@ -59,7 +62,8 @@ public class QualifiedIdentifier extends SpelNodeImpl {
StringBuilder sb = new StringBuilder();
if (this.value != null) {
sb.append(this.value.getValue());
} else {
}
else {
for (int i = 0; i < getChildCount(); i++) {
if (i > 0) {
sb.append(".");

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
/**
* Expression language AST node that represents a real literal.
*
* @author Andy Clement
* @since 3.0
*/
@ -26,11 +28,13 @@ public class RealLiteral extends Literal {
private final TypedValue value;
public RealLiteral(String payload, int pos, double value) {
super(payload, pos);
this.value = new TypedValue(value);
}
@Override
public TypedValue getLiteralValue() {
return this.value;

View File

@ -47,14 +47,18 @@ import org.springframework.util.ObjectUtils;
*/
public class Selection extends SpelNodeImpl {
public final static int ALL = 0; // ?[]
public final static int FIRST = 1; // ^[]
public final static int LAST = 2; // $[]
public static final int ALL = 0; // ?[]
public static final int FIRST = 1; // ^[]
public static final int LAST = 2; // $[]
private final int variant;
private final boolean nullSafe;
public Selection(boolean nullSafe, int variant,int pos,SpelNodeImpl expression) {
public Selection(boolean nullSafe, int variant, int pos, SpelNodeImpl expression) {
super(pos, expression != null ? new SpelNodeImpl[] { expression }
: new SpelNodeImpl[] {});
Assert.notNull(expression, "Expression must not be null");
@ -72,7 +76,7 @@ public class Selection extends SpelNodeImpl {
TypedValue op = state.getActiveContextObject();
Object operand = op.getValue();
SpelNodeImpl selectionCriteria = children[0];
SpelNodeImpl selectionCriteria = this.children[0];
if (operand instanceof Map) {
Map<?, ?> mapdata = (Map<?, ?>) operand;
// TODO don't lose generic info for the new map
@ -85,32 +89,38 @@ public class Selection extends SpelNodeImpl {
Object o = selectionCriteria.getValueInternal(state).getValue();
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
if (variant == FIRST) {
if (this.variant == FIRST) {
result.put(entry.getKey(),entry.getValue());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
}
result.put(entry.getKey(),entry.getValue());
lastKey = entry.getKey();
}
} else {
}
else {
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
}
} finally {
}
finally {
state.popActiveContextObject();
}
}
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(null),this);
}
if (variant == LAST) {
if (this.variant == LAST) {
Map<Object, Object> resultMap = new HashMap<Object, Object>();
Object lastValue = result.get(lastKey);
resultMap.put(lastKey,lastValue);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultMap),this);
}
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
} else if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) {
}
if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) {
List<Object> data = new ArrayList<Object>();
Collection<?> c = (operand instanceof Collection) ?
(Collection<?>) operand : Arrays.asList(ObjectUtils.toObjectArray(operand));
@ -124,64 +134,64 @@ public class Selection extends SpelNodeImpl {
Object o = selectionCriteria.getValueInternal(state).getValue();
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
if (variant == FIRST) {
if (this.variant == FIRST) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(element),this);
}
result.add(element);
}
} else {
}
else {
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
}
idx++;
} finally {
}
finally {
state.exitScope();
state.popActiveContextObject();
}
}
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) {
return ValueRef.NullValueRef.instance;
}
if (variant == LAST) {
if (this.variant == LAST) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result.get(result.size() - 1)),this);
}
if (operand instanceof Collection) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
}
else {
Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementTypeDescriptor().getType());
Object resultArray = Array.newInstance(elementType, result.size());
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray),this);
}
} else {
if (operand==null) {
if (nullSafe) {
return ValueRef.NullValueRef.instance;
} else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
"null");
}
} else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
operand.getClass().getName());
}
Class<?> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementTypeDescriptor().getType());
Object resultArray = Array.newInstance(elementType, result.size());
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray),this);
}
if (operand==null) {
if (this.nullSafe) {
return ValueRef.NullValueRef.instance;
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.INVALID_TYPE_FOR_SELECTION, "null");
}
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.INVALID_TYPE_FOR_SELECTION, operand.getClass().getName());
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
switch (variant) {
case ALL:
sb.append("?[");
break;
case FIRST:
sb.append("^[");
break;
case LAST:
sb.append("$[");
break;
switch (this.variant) {
case ALL:
sb.append("?[");
break;
case FIRST:
sb.append("^[");
break;
case LAST:
sb.append("$[");
break;
}
return sb.append(getChild(0).toStringAST()).append("]").toString();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,7 +27,8 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
/**
* The common supertype of all AST nodes in a parsed Spring Expression Language format expression.
* The common supertype of all AST nodes in a parsed Spring Expression Language format
* expression.
*
* @author Andy Clement
* @since 3.0
@ -37,9 +38,12 @@ public abstract class SpelNodeImpl implements SpelNode {
private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
protected int pos; // start = top 16bits, end = bottom 16bits
protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
private SpelNodeImpl parent;
public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
this.pos = pos;
// pos combines start and end so can never be zero because tokens cannot be zero length
@ -52,11 +56,14 @@ public abstract class SpelNodeImpl implements SpelNode {
}
}
protected SpelNodeImpl getPreviousChild() {
SpelNodeImpl result = null;
if (parent != null) {
for (SpelNodeImpl child : parent.children) {
if (this==child) break;
if (this.parent != null) {
for (SpelNodeImpl child : this.parent.children) {
if (this==child) {
break;
}
result = child;
}
}
@ -67,21 +74,22 @@ public abstract class SpelNodeImpl implements SpelNode {
* @return true if the next child is one of the specified classes
*/
protected boolean nextChildIs(Class... clazzes) {
if (parent!=null) {
SpelNodeImpl[] peers = parent.children;
for (int i=0,max=peers.length;i<max;i++) {
if (peers[i]==this) {
if ((i+1)>=max) {
return false;
} else {
Class clazz = peers[i+1].getClass();
for (Class desiredClazz: clazzes) {
if (clazz.equals(desiredClazz)) {
return true;
}
}
if (this.parent != null) {
SpelNodeImpl[] peers = this.parent.children;
for (int i = 0, max = peers.length; i < max; i++) {
if (peers[i] == this) {
if ((i + 1) >= max) {
return false;
}
Class clazz = peers[i + 1].getClass();
for (Class desiredClazz : clazzes) {
if (clazz.equals(desiredClazz)) {
return true;
}
}
return false;
}
}
}
@ -92,7 +100,8 @@ public abstract class SpelNodeImpl implements SpelNode {
public final Object getValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState != null) {
return getValueInternal(expressionState).getValue();
} else {
}
else {
// configuration not set - does that matter?
return getValue(new ExpressionState(new StandardEvaluationContext()));
}
@ -102,7 +111,8 @@ public abstract class SpelNodeImpl implements SpelNode {
public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState != null) {
return getValueInternal(expressionState);
} else {
}
else {
// configuration not set - does that matter?
return getTypedValue(new ExpressionState(new StandardEvaluationContext()));
}
@ -116,17 +126,18 @@ public abstract class SpelNodeImpl implements SpelNode {
@Override
public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
throw new SpelEvaluationException(getStartPosition(),
SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
}
@Override
public SpelNode getChild(int index) {
return children[index];
return this.children[index];
}
@Override
public int getChildCount() {
return children.length;
return this.children.length;
}
@Override
@ -148,15 +159,15 @@ public abstract class SpelNodeImpl implements SpelNode {
@Override
public int getStartPosition() {
return (pos>>16);
return (this.pos>>16);
}
@Override
public int getEndPosition() {
return (pos&0xffff);
return (this.pos&0xffff);
}
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
throw new SpelEvaluationException(pos,SpelMessage.NOT_ASSIGNABLE,toStringAST());
throw new SpelEvaluationException(this.pos,SpelMessage.NOT_ASSIGNABLE,toStringAST());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
/**
* Expression language AST node that represents a string literal.
*
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
@ -27,6 +29,7 @@ public class StringLiteral extends Literal {
private final TypedValue value;
public StringLiteral(String payload, int pos, String value) {
super(payload,pos);
// TODO should these have been skipped being created by the parser rules? or not?
@ -34,6 +37,7 @@ public class StringLiteral extends Literal {
this.value = new TypedValue(value.replaceAll("''", "'").replaceAll("\"\"", "\""));
}
@Override
public TypedValue getLiteralValue() {
return this.value;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,22 +37,24 @@ public class Ternary extends SpelNodeImpl {
}
/**
* Evaluate the condition and if true evaluate the first alternative, otherwise evaluate the second alternative.
* 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
* @throws EvaluationException if the condition does not evaluate correctly to a
* boolean or there is a problem executing the chosen alternative
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Boolean value = children[0].getValue(state, Boolean.class);
Boolean value = this.children[0].getValue(state, Boolean.class);
if (value == null) {
throw new SpelEvaluationException(getChild(0).getStartPosition(),
SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
}
if (value.booleanValue()) {
return children[1].getValueInternal(state);
} else {
return children[2].getValueInternal(state);
return this.children[1].getValueInternal(state);
}
else {
return this.children[2].getValueInternal(state);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author 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,26 +16,45 @@
package org.springframework.expression.spel.ast;
/**
* Captures primitive types and their corresponding class objects, plus one special entry that represents all reference
* (non-primitive) types.
* Captures primitive types and their corresponding class objects, plus one special entry
* that represents all reference (non-primitive) types.
*
* @author Andy Clement
*/
public enum TypeCode {
OBJECT(Object.class), BOOLEAN(Boolean.TYPE), BYTE(Byte.TYPE), CHAR(Character.TYPE), //
SHORT(Short.TYPE), INT(Integer.TYPE), LONG(Long.TYPE), FLOAT(Float.TYPE), DOUBLE(Double.TYPE);
OBJECT(Object.class),
BOOLEAN(Boolean.TYPE),
BYTE(Byte.TYPE),
CHAR(Character.TYPE),
SHORT(Short.TYPE),
INT(Integer.TYPE),
LONG(Long.TYPE),
FLOAT(Float.TYPE),
DOUBLE(Double.TYPE);
private Class<?> type;
TypeCode(Class<?> type) {
this.type = type;
}
public Class<?> getType() {
return type;
return this.type;
}
public static TypeCode forName(String name) {
String searchingFor = name.toUpperCase();
TypeCode[] tcs = values();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,21 +29,23 @@ import org.springframework.expression.spel.ExpressionState;
*/
public class TypeReference extends SpelNodeImpl {
private int dimensions;
private final int dimensions;
public TypeReference(int pos,SpelNodeImpl qualifiedId) {
public TypeReference(int pos, SpelNodeImpl qualifiedId) {
this(pos,qualifiedId,0);
}
public TypeReference(int pos,SpelNodeImpl qualifiedId,int dims) {
public TypeReference(int pos, SpelNodeImpl qualifiedId, int dims) {
super(pos,qualifiedId);
this.dimensions = dims;
}
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// TODO possible optimization here if we cache the discovered type reference, but can we do that?
String typename = (String) children[0].getValueInternal(state).getValue();
String typename = (String) this.children[0].getValueInternal(state).getValue();
if (typename.indexOf(".") == -1 && Character.isLowerCase(typename.charAt(0))) {
TypeCode tc = TypeCode.valueOf(typename.toUpperCase());
if (tc != TypeCode.OBJECT) {
@ -59,8 +61,8 @@ public class TypeReference extends SpelNodeImpl {
}
private Class makeArrayIfNecessary(Class clazz) {
if (dimensions!=0) {
for (int i=0;i<dimensions;i++) {
if (this.dimensions!=0) {
for (int i=0;i<this.dimensions;i++) {
Object o = Array.newInstance(clazz, 0);
clazz = o.getClass();
}
@ -73,7 +75,7 @@ public class TypeReference extends SpelNodeImpl {
StringBuilder sb = new StringBuilder();
sb.append("T(");
sb.append(getChild(0).toStringAST());
for (int d=0;d<dimensions;d++) {
for (int d=0;d<this.dimensions;d++) {
sb.append("[]");
}
sb.append(")");

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,6 +33,27 @@ import org.springframework.expression.spel.SpelMessage;
*/
public interface ValueRef {
/**
* Returns the value this ValueRef points to, it should not require expression
* component re-evaluation.
* @return the value
*/
TypedValue getValue();
/**
* Sets the value this ValueRef points to, it should not require expression component
* re-evaluation.
* @param newValue the new value
*/
void setValue(Object newValue);
/**
* Indicates whether calling setValue(Object) is supported.
* @return true if setValue() is supported for this value reference.
*/
boolean isWritable();
/**
* A ValueRef for the null value.
*/
@ -60,13 +81,14 @@ public interface ValueRef {
}
/**
* A ValueRef holder for a single value, which cannot be set.
*/
static class TypedValueHolderValueRef implements ValueRef {
private TypedValue typedValue;
private SpelNodeImpl node; // used only for error reporting
private final TypedValue typedValue;
private final SpelNodeImpl node; // used only for error reporting
public TypedValueHolderValueRef(TypedValue typedValue,SpelNodeImpl node) {
this.typedValue = typedValue;
@ -91,23 +113,4 @@ public interface ValueRef {
}
/**
* Returns the value this ValueRef points to, it should not require expression
* component re-evaluation.
* @return the value
*/
TypedValue getValue();
/**
* Sets the value this ValueRef points to, it should not require expression component
* re-evaluation.
* @param newValue the new value
*/
void setValue(Object newValue);
/**
* Indicates whether calling setValue(Object) is supported.
* @return true if setValue() is supported for this value reference.
*/
boolean isWritable();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,7 +22,8 @@ import org.springframework.expression.spel.ExpressionState;
import org.springframework.expression.spel.SpelEvaluationException;
/**
* Represents a variable reference, eg. #someVar. Note this is different to a *local* variable like $someVar
* Represents a variable reference, eg. #someVar. Note this is different to a *local*
* variable like $someVar
*
* @author Andy Clement
* @since 3.0
@ -30,46 +31,17 @@ import org.springframework.expression.spel.SpelEvaluationException;
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 static final String THIS = "this"; // currently active context object
private static final String ROOT = "root"; // root context object
private final String name;
public VariableReference(String variableName, int pos) {
super(pos);
name = variableName;
}
class VariableRef implements ValueRef {
private String name;
private TypedValue value;
private EvaluationContext eContext;
public VariableRef(String name, TypedValue value,
EvaluationContext evaluationContext) {
this.name = name;
this.value = value;
this.eContext = evaluationContext;
}
@Override
public TypedValue getValue() {
return value;
}
@Override
public void setValue(Object newValue) {
eContext.setVariable(name, newValue);
}
@Override
public boolean isWritable() {
return true;
}
this.name = variableName;
}
@ -114,4 +86,39 @@ public class VariableReference extends SpelNodeImpl {
return !(this.name.equals(THIS) || this.name.equals(ROOT));
}
class VariableRef implements ValueRef {
private final String name;
private final TypedValue value;
private final EvaluationContext evaluationContext;
public VariableRef(String name, TypedValue value,
EvaluationContext evaluationContext) {
this.name = name;
this.value = value;
this.evaluationContext = evaluationContext;
}
@Override
public TypedValue getValue() {
return this.value;
}
@Override
public void setValue(Object newValue) {
this.evaluationContext.setVariable(this.name, newValue);
}
@Override
public boolean isWritable() {
return true;
}
}
}

View File

@ -44,6 +44,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+");
// The expression being parsed
private String expressionString;
@ -57,9 +58,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private int tokenStreamPointer;
// For rules that build nodes, they are stacked here for return
private Stack<SpelNodeImpl> constructedNodes = new Stack<SpelNodeImpl>();
private final Stack<SpelNodeImpl> constructedNodes = new Stack<SpelNodeImpl>();
private SpelParserConfiguration configuration;
private final SpelParserConfiguration configuration;
/**
@ -77,16 +78,16 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
this.expressionString = expressionString;
Tokenizer tokenizer = new Tokenizer(expressionString);
tokenizer.process();
tokenStream = tokenizer.getTokens();
tokenStreamLength = tokenStream.size();
tokenStreamPointer = 0;
constructedNodes.clear();
this.tokenStream = tokenizer.getTokens();
this.tokenStreamLength = this.tokenStream.size();
this.tokenStreamPointer = 0;
this.constructedNodes.clear();
SpelNodeImpl ast = eatExpression();
if (moreTokens()) {
throw new SpelParseException(peekToken().startpos,SpelMessage.MORE_INPUT,toString(nextToken()));
}
Assert.isTrue(constructedNodes.isEmpty());
return new SpelExpression(expressionString, ast, configuration);
Assert.isTrue(this.constructedNodes.isEmpty());
return new SpelExpression(expressionString, ast, this.configuration);
}
catch (InternalParseException ipe) {
throw ipe.getCause();
@ -110,7 +111,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
nextToken();
SpelNodeImpl assignedValue = eatLogicalOrExpression();
return new Assign(toPos(t),expr,assignedValue);
} else if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
}
if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
if (expr==null) {
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-2));
}
@ -120,7 +123,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
valueIfNull = new NullLiteral(toPos(t.startpos+1,t.endpos+1));
}
return new Elvis(toPos(t),expr,valueIfNull);
} else if (t.kind==TokenKind.QMARK) { // a?b:c
}
if (t.kind==TokenKind.QMARK) { // a?b:c
if (expr==null) {
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1));
}
@ -167,31 +172,38 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
SpelNodeImpl rhExpr = eatSumExpression();
checkOperands(t,expr,rhExpr);
TokenKind tk = relationalOperatorToken.kind;
if (relationalOperatorToken.isNumericRelationalOperator()) {
int pos = toPos(t);
if (tk==TokenKind.GT) {
return new OpGT(pos,expr,rhExpr);
} else if (tk==TokenKind.LT) {
return new OpLT(pos,expr,rhExpr);
} else if (tk==TokenKind.LE) {
return new OpLE(pos,expr,rhExpr);
} else if (tk==TokenKind.GE) {
return new OpGE(pos,expr,rhExpr);
} else if (tk == TokenKind.EQ) {
return new OpEQ(pos,expr,rhExpr);
} else {
Assert.isTrue(tk == TokenKind.NE);
return new OpNE(pos,expr,rhExpr);
if (tk == TokenKind.GT) {
return new OpGT(pos, expr, rhExpr);
}
if (tk == TokenKind.LT) {
return new OpLT(pos, expr, rhExpr);
}
if (tk == TokenKind.LE) {
return new OpLE(pos, expr, rhExpr);
}
if (tk == TokenKind.GE) {
return new OpGE(pos, expr, rhExpr);
}
if (tk == TokenKind.EQ) {
return new OpEQ(pos, expr, rhExpr);
}
Assert.isTrue(tk == TokenKind.NE);
return new OpNE(pos, expr, rhExpr);
}
if (tk==TokenKind.INSTANCEOF) {
return new OperatorInstanceof(toPos(t),expr,rhExpr);
} else if (tk==TokenKind.MATCHES) {
return new OperatorMatches(toPos(t),expr,rhExpr);
} else {
Assert.isTrue(tk==TokenKind.BETWEEN);
return new org.springframework.expression.spel.ast.OperatorBetween(toPos(t),expr,rhExpr);
if (tk == TokenKind.INSTANCEOF) {
return new OperatorInstanceof(toPos(t), expr, rhExpr);
}
if (tk == TokenKind.MATCHES) {
return new OperatorMatches(toPos(t), expr, rhExpr);
}
Assert.isTrue(tk == TokenKind.BETWEEN);
return new OperatorBetween(toPos(t), expr, rhExpr);
}
return expr;
}
@ -199,14 +211,15 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
//sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*;
private SpelNodeImpl eatSumExpression() {
SpelNodeImpl expr = eatProductExpression();
while (peekToken(TokenKind.PLUS,TokenKind.MINUS,TokenKind.INC)) {
while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) {
Token t = nextToken();//consume PLUS or MINUS or INC
SpelNodeImpl rhExpr = eatProductExpression();
checkRightOperand(t,rhExpr);
if (t.kind==TokenKind.PLUS) {
expr = new OpPlus(toPos(t),expr,rhExpr);
} else if (t.kind==TokenKind.MINUS) {
expr = new OpMinus(toPos(t),expr,rhExpr);
if (t.kind == TokenKind.PLUS) {
expr = new OpPlus(toPos(t), expr, rhExpr);
}
else if (t.kind == TokenKind.MINUS) {
expr = new OpMinus(toPos(t), expr, rhExpr);
}
}
return expr;
@ -215,17 +228,19 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ;
private SpelNodeImpl eatProductExpression() {
SpelNodeImpl expr = eatPowerIncDecExpression();
while (peekToken(TokenKind.STAR,TokenKind.DIV,TokenKind.MOD)) {
while (peekToken(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) {
Token t = nextToken(); // consume STAR/DIV/MOD
SpelNodeImpl rhExpr = eatPowerIncDecExpression();
checkOperands(t,expr,rhExpr);
if (t.kind==TokenKind.STAR) {
expr = new OpMultiply(toPos(t),expr,rhExpr);
} else if (t.kind==TokenKind.DIV) {
expr = new OpDivide(toPos(t),expr,rhExpr);
} else {
Assert.isTrue(t.kind==TokenKind.MOD);
expr = new OpModulus(toPos(t),expr,rhExpr);
if (t.kind == TokenKind.STAR) {
expr = new OpMultiply(toPos(t), expr, rhExpr);
}
else if (t.kind == TokenKind.DIV) {
expr = new OpDivide(toPos(t), expr, rhExpr);
}
else {
Assert.isTrue(t.kind == TokenKind.MOD);
expr = new OpModulus(toPos(t), expr, rhExpr);
}
}
return expr;
@ -239,41 +254,45 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
SpelNodeImpl rhExpr = eatUnaryExpression();
checkRightOperand(t,rhExpr);
return new OperatorPower(toPos(t),expr, rhExpr);
} else if (expr!=null && peekToken(TokenKind.INC,TokenKind.DEC)) {
}
if (expr!=null && peekToken(TokenKind.INC,TokenKind.DEC)) {
Token t = nextToken();//consume INC/DEC
if (t.getKind()==TokenKind.INC) {
return new OpInc(toPos(t),true,expr);
} else {
return new OpDec(toPos(t),true,expr);
}
return new OpDec(toPos(t),true,expr);
}
return expr;
}
// unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ;
private SpelNodeImpl eatUnaryExpression() {
if (peekToken(TokenKind.PLUS,TokenKind.MINUS,TokenKind.NOT)) {
if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) {
Token t = nextToken();
SpelNodeImpl expr = eatUnaryExpression();
if (t.kind==TokenKind.NOT) {
return new OperatorNot(toPos(t),expr);
} else if (t.kind==TokenKind.PLUS) {
return new OpPlus(toPos(t),expr);
} else {
Assert.isTrue(t.kind==TokenKind.MINUS);
return new OpMinus(toPos(t),expr);
if (t.kind == TokenKind.NOT) {
return new OperatorNot(toPos(t), expr);
}
} else if (peekToken(TokenKind.INC,TokenKind.DEC)) {
Token t = nextToken();
SpelNodeImpl expr = eatUnaryExpression();
if (t.getKind()==TokenKind.INC) {
return new OpInc(toPos(t),false,expr);
} else {
return new OpDec(toPos(t),false,expr);
if (t.kind == TokenKind.PLUS) {
return new OpPlus(toPos(t), expr);
}
} else {
return eatPrimaryExpression();
Assert.isTrue(t.kind == TokenKind.MINUS);
return new OpMinus(toPos(t), expr);
}
if (peekToken(TokenKind.INC, TokenKind.DEC)) {
Token t = nextToken();
SpelNodeImpl expr = eatUnaryExpression();
if (t.getKind() == TokenKind.INC) {
return new OpInc(toPos(t), false, expr);
}
return new OpDec(toPos(t), false, expr);
}
return eatPrimaryExpression();
}
// primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?);
@ -284,11 +303,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
while (maybeEatNode()) {
nodes.add(pop());
}
if (nodes.size()==1) {
if (nodes.size() == 1) {
return nodes.get(0);
} else {
return new CompoundExpression(toPos(start.getStartPosition(),nodes.get(nodes.size()-1).getEndPosition()),nodes.toArray(new SpelNodeImpl[nodes.size()]));
}
return new CompoundExpression(toPos(start.getStartPosition(),
nodes.get(nodes.size() - 1).getEndPosition()),
nodes.toArray(new SpelNodeImpl[nodes.size()]));
}
// node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+;
@ -299,6 +319,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
} else {
expr = maybeEatNonDottedNode();
}
if (expr==null) {
return false;
} else {
@ -328,15 +349,19 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// ;
private SpelNodeImpl eatDottedNode() {
Token t = nextToken();// it was a '.' or a '?.'
boolean nullSafeNavigation = t.kind==TokenKind.SAFE_NAVI;
if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() || maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) {
boolean nullSafeNavigation = t.kind == TokenKind.SAFE_NAVI;
if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar()
|| maybeEatProjection(nullSafeNavigation)
|| maybeEatSelection(nullSafeNavigation)) {
return pop();
}
if (peekToken()==null) {
if (peekToken() == null) {
// unexpectedly ran out of data
raiseInternalException(t.startpos,SpelMessage.OOD);
} else {
raiseInternalException(t.startpos,SpelMessage.UNEXPECTED_DATA_AFTER_DOT,toString(peekToken()));
raiseInternalException(t.startpos, SpelMessage.OOD);
}
else {
raiseInternalException(t.startpos, SpelMessage.UNEXPECTED_DATA_AFTER_DOT,
toString(peekToken()));
}
return null;
}
@ -354,13 +379,15 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
Token t = nextToken();
Token functionOrVariableName = eatToken(TokenKind.IDENTIFIER);
SpelNodeImpl[] args = maybeEatMethodArgs();
if (args==null) {
push(new VariableReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos)));
return true;
} else {
push(new FunctionReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos),args));
if (args == null) {
push(new VariableReference(functionOrVariableName.data, toPos(t.startpos,
functionOrVariableName.endpos)));
return true;
}
push(new FunctionReference(functionOrVariableName.data, toPos(t.startpos,
functionOrVariableName.endpos), args));
return true;
}
// methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!;
@ -376,7 +403,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private void eatConstructorArgs(List<SpelNodeImpl> accumulatedArguments) {
if (!peekToken(TokenKind.LPAREN)) {
throw new InternalParseException(new SpelParseException(expressionString,positionOf(peekToken()),SpelMessage.MISSING_CONSTRUCTOR_ARGS));
throw new InternalParseException(new SpelParseException(this.expressionString,positionOf(peekToken()),SpelMessage.MISSING_CONSTRUCTOR_ARGS));
}
consumeArguments(accumulatedArguments);
eatToken(TokenKind.RPAREN);
@ -391,27 +418,28 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
do {
nextToken();// consume ( (first time through) or comma (subsequent times)
Token t = peekToken();
if (t==null) {
raiseInternalException(pos,SpelMessage.RUN_OUT_OF_ARGUMENTS);
if (t == null) {
raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS);
}
if (t.kind!=TokenKind.RPAREN) {
if (t.kind != TokenKind.RPAREN) {
accumulatedArguments.add(eatExpression());
}
next = peekToken();
} while (next!=null && next.kind==TokenKind.COMMA);
if (next==null) {
raiseInternalException(pos,SpelMessage.RUN_OUT_OF_ARGUMENTS);
}
while (next != null && next.kind == TokenKind.COMMA);
if (next == null) {
raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS);
}
}
private int positionOf(Token t) {
if (t==null) {
if (t == null) {
// if null assume the problem is because the right token was
// not found at the end of the expression
return expressionString.length();
} else {
return t.startpos;
return this.expressionString.length();
}
return t.startpos;
}
//startNode
@ -428,17 +456,26 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private SpelNodeImpl eatStartNode() {
if (maybeEatLiteral()) {
return pop();
} else if (maybeEatParenExpression()) {
}
else if (maybeEatParenExpression()) {
return pop();
} else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) {
}
else if (maybeEatTypeReference() || maybeEatNullReference()
|| maybeEatConstructorReference() || maybeEatMethodOrProperty(false)
|| maybeEatFunctionOrVar()) {
return pop();
} else if (maybeEatBeanReference()) {
}
else if (maybeEatBeanReference()) {
return pop();
} else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
}
else if (maybeEatProjection(false) || maybeEatSelection(false)
|| maybeEatIndexer()) {
return pop();
} else if (maybeEatInlineList()) {
}
else if (maybeEatInlineList()) {
return pop();
} else {
}
else {
return null;
}
}
@ -453,16 +490,19 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
if (peekToken(TokenKind.IDENTIFIER)) {
beanNameToken = eatToken(TokenKind.IDENTIFIER);
beanname = beanNameToken.data;
} else if (peekToken(TokenKind.LITERAL_STRING)) {
}
else if (peekToken(TokenKind.LITERAL_STRING)) {
beanNameToken = eatToken(TokenKind.LITERAL_STRING);
beanname = beanNameToken.stringValue();
beanname = beanname.substring(1, beanname.length() - 1);
} else {
raiseInternalException(beanRefToken.startpos,SpelMessage.INVALID_BEAN_REFERENCE);
}
else {
raiseInternalException(beanRefToken.startpos,
SpelMessage.INVALID_BEAN_REFERENCE);
}
BeanReference beanReference = new BeanReference(toPos(beanNameToken),beanname);
constructedNodes.push(beanReference);
this.constructedNodes.push(beanReference);
return true;
}
return false;
@ -485,7 +525,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
dims++;
}
eatToken(TokenKind.RPAREN);
constructedNodes.push(new TypeReference(toPos(typeName),node,dims));
this.constructedNodes.push(new TypeReference(toPos(typeName),node,dims));
return true;
}
return false;
@ -498,7 +538,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
return false;
}
nextToken();
constructedNodes.push(new NullLiteral(toPos(nullToken)));
this.constructedNodes.push(new NullLiteral(toPos(nullToken)));
return true;
}
return false;
@ -507,46 +547,48 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
//projection: PROJECT^ expression RCURLY!;
private boolean maybeEatProjection(boolean nullSafeNavigation) {
Token t = peekToken();
if (!peekToken(TokenKind.PROJECT,true)) {
if (!peekToken(TokenKind.PROJECT, true)) {
return false;
}
SpelNodeImpl expr = eatExpression();
eatToken(TokenKind.RSQUARE);
constructedNodes.push(new Projection(nullSafeNavigation, toPos(t), expr));
this.constructedNodes.push(new Projection(nullSafeNavigation, toPos(t), expr));
return true;
}
// list = LCURLY (element (COMMA element)*) RCURLY
private boolean maybeEatInlineList() {
Token t = peekToken();
if (!peekToken(TokenKind.LCURLY,true)) {
if (!peekToken(TokenKind.LCURLY, true)) {
return false;
}
SpelNodeImpl expr = null;
Token closingCurly = peekToken();
if (peekToken(TokenKind.RCURLY,true)) {
if (peekToken(TokenKind.RCURLY, true)) {
// empty list '[]'
expr = new InlineList(toPos(t.startpos,closingCurly.endpos));
} else {
}
else {
List<SpelNodeImpl> listElements = new ArrayList<SpelNodeImpl>();
do {
listElements.add(eatExpression());
} while (peekToken(TokenKind.COMMA,true));
closingCurly = eatToken(TokenKind.RCURLY);
expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
}
constructedNodes.push(expr);
this.constructedNodes.push(expr);
return true;
}
private boolean maybeEatIndexer() {
Token t = peekToken();
if (!peekToken(TokenKind.LSQUARE,true)) {
if (!peekToken(TokenKind.LSQUARE, true)) {
return false;
}
SpelNodeImpl expr = eatExpression();
eatToken(TokenKind.RSQUARE);
constructedNodes.push(new Indexer(toPos(t),expr));
this.constructedNodes.push(new Indexer(toPos(t),expr));
return true;
}
@ -561,12 +603,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
raiseInternalException(toPos(t), SpelMessage.MISSING_SELECTION_EXPRESSION);
}
eatToken(TokenKind.RSQUARE);
if (t.kind==TokenKind.SELECT_FIRST) {
constructedNodes.push(new Selection(nullSafeNavigation,Selection.FIRST,toPos(t),expr));
} else if (t.kind==TokenKind.SELECT_LAST) {
constructedNodes.push(new Selection(nullSafeNavigation,Selection.LAST,toPos(t),expr));
} else {
constructedNodes.push(new Selection(nullSafeNavigation,Selection.ALL,toPos(t),expr));
if (t.kind == TokenKind.SELECT_FIRST) {
this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.FIRST, toPos(t), expr));
}
else if (t.kind == TokenKind.SELECT_LAST) {
this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.LAST, toPos(t), expr));
}
else {
this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.ALL, toPos(t), expr));
}
return true;
}
@ -587,7 +631,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
if(qualifiedIdPieces.isEmpty()) {
if(node == null) {
raiseInternalException( expressionString.length(), SpelMessage.OOD);
raiseInternalException( this.expressionString.length(), SpelMessage.OOD);
}
raiseInternalException(node.startpos, SpelMessage.NOT_EXPECTED_TOKEN,
"qualified ID", node.getKind().toString().toLowerCase());
@ -617,13 +661,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// property
push(new PropertyOrFieldReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName)));
return true;
} else {
// methodreference
push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args));
// TODO what is the end position for a method reference? the name or the last arg?
return true;
}
// methodreference
push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args));
// TODO what is the end position for a method reference? the name or the last arg?
return true;
}
return false;
}
@ -642,7 +686,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
while (peekToken(TokenKind.LSQUARE,true)) {
if (!peekToken(TokenKind.RSQUARE)) {
dimensions.add(eatExpression());
} else {
}
else {
dimensions.add(null);
}
eatToken(TokenKind.RSQUARE);
@ -652,11 +697,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
push(new ConstructorReference(toPos(newToken), dimensions.toArray(new SpelNodeImpl[dimensions.size()]),
nodes.toArray(new SpelNodeImpl[nodes.size()])));
} else {
}
else {
// regular constructor invocation
eatConstructorArgs(nodes);
// TODO correct end position?
push(new ConstructorReference(toPos(newToken), nodes.toArray(new SpelNodeImpl[nodes.size()])));
push(new ConstructorReference(toPos(newToken),
nodes.toArray(new SpelNodeImpl[nodes.size()])));
}
return true;
}
@ -664,11 +711,11 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
private void push(SpelNodeImpl newNode) {
constructedNodes.push(newNode);
this.constructedNodes.push(newNode);
}
private SpelNodeImpl pop() {
return constructedNodes.pop();
return this.constructedNodes.pop();
}
// literal
@ -684,25 +731,34 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
if (t==null) {
return false;
}
if (t.kind==TokenKind.LITERAL_INT) {
if (t.kind == TokenKind.LITERAL_INT) {
push(Literal.getIntLiteral(t.data, toPos(t), 10));
} else if (t.kind==TokenKind.LITERAL_LONG) {
}
else if (t.kind == TokenKind.LITERAL_LONG) {
push(Literal.getLongLiteral(t.data, toPos(t), 10));
} else if (t.kind==TokenKind.LITERAL_HEXINT) {
}
else if (t.kind == TokenKind.LITERAL_HEXINT) {
push(Literal.getIntLiteral(t.data, toPos(t), 16));
} else if (t.kind==TokenKind.LITERAL_HEXLONG) {
}
else if (t.kind == TokenKind.LITERAL_HEXLONG) {
push(Literal.getLongLiteral(t.data, toPos(t), 16));
} else if (t.kind==TokenKind.LITERAL_REAL) {
push(Literal.getRealLiteral(t.data, toPos(t),false));
} else if (t.kind==TokenKind.LITERAL_REAL_FLOAT) {
}
else if (t.kind == TokenKind.LITERAL_REAL) {
push(Literal.getRealLiteral(t.data, toPos(t), false));
}
else if (t.kind == TokenKind.LITERAL_REAL_FLOAT) {
push(Literal.getRealLiteral(t.data, toPos(t), true));
} else if (peekIdentifierToken("true")) {
push(new BooleanLiteral(t.data,toPos(t),true));
} else if (peekIdentifierToken("false")) {
push(new BooleanLiteral(t.data,toPos(t),false));
} else if (t.kind==TokenKind.LITERAL_STRING) {
push(new StringLiteral(t.data,toPos(t),t.data));
} else {
}
else if (peekIdentifierToken("true")) {
push(new BooleanLiteral(t.data, toPos(t), true));
}
else if (peekIdentifierToken("false")) {
push(new BooleanLiteral(t.data, toPos(t), false));
}
else if (t.kind == TokenKind.LITERAL_STRING) {
push(new StringLiteral(t.data, toPos(t), t.data));
}
else {
return false;
}
nextToken();
@ -717,7 +773,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
eatToken(TokenKind.RPAREN);
push(expr);
return true;
} else {
}
else {
return false;
}
}
@ -737,9 +794,11 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
String idString = t.stringValue();
if (idString.equalsIgnoreCase("instanceof")) {
return t.asInstanceOfToken();
} else if (idString.equalsIgnoreCase("matches")) {
}
if (idString.equalsIgnoreCase("matches")) {
return t.asMatchesToken();
} else if (idString.equalsIgnoreCase("between")) {
}
if (idString.equalsIgnoreCase("between")) {
return t.asBetweenToken();
}
}
@ -749,7 +808,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private Token eatToken(TokenKind expectedKind) {
Token t = nextToken();
if (t==null) {
raiseInternalException( expressionString.length(), SpelMessage.OOD);
raiseInternalException( this.expressionString.length(), SpelMessage.OOD);
}
if (t.kind!=expectedKind) {
raiseInternalException(t.startpos,SpelMessage.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),t.getKind().toString().toLowerCase());
@ -768,32 +827,36 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
Token t = peekToken();
if (t.kind==desiredTokenKind) {
if (consumeIfMatched) {
tokenStreamPointer++;
this.tokenStreamPointer++;
}
return true;
} else {
if (desiredTokenKind == TokenKind.IDENTIFIER) {
// might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier
// The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum
if (t.kind.ordinal()>=TokenKind.DIV.ordinal() && t.kind.ordinal()<=TokenKind.NOT.ordinal() && t.data!=null) {
// if t.data were null, we'd know it wasn't the textual form, it was the symbol form
return true;
}
}
return false;
}
if (desiredTokenKind == TokenKind.IDENTIFIER) {
// might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier
// The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum
if (t.kind.ordinal()>=TokenKind.DIV.ordinal() && t.kind.ordinal()<=TokenKind.NOT.ordinal() && t.data!=null) {
// if t.data were null, we'd know it wasn't the textual form, it was the symbol form
return true;
}
}
return false;
}
private boolean peekToken(TokenKind possible1,TokenKind possible2) {
if (!moreTokens()) return false;
if (!moreTokens()) {
return false;
}
Token t = peekToken();
return t.kind==possible1 || t.kind==possible2;
return t.kind == possible1 || t.kind == possible2;
}
private boolean peekToken(TokenKind possible1,TokenKind possible2, TokenKind possible3) {
if (!moreTokens()) return false;
if (!moreTokens()) {
return false;
}
Token t = peekToken();
return t.kind==possible1 || t.kind==possible2 || t.kind==possible3;
return t.kind == possible1 || t.kind == possible2 || t.kind == possible3;
}
private boolean peekIdentifierToken(String identifierString) {
@ -805,39 +868,42 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
private boolean peekSelectToken() {
if (!moreTokens()) return false;
if (!moreTokens()) {
return false;
}
Token t = peekToken();
return t.kind==TokenKind.SELECT || t.kind==TokenKind.SELECT_FIRST || t.kind==TokenKind.SELECT_LAST;
return t.kind == TokenKind.SELECT || t.kind == TokenKind.SELECT_FIRST
|| t.kind == TokenKind.SELECT_LAST;
}
private boolean moreTokens() {
return tokenStreamPointer<tokenStream.size();
return this.tokenStreamPointer<this.tokenStream.size();
}
private Token nextToken() {
if (tokenStreamPointer>=tokenStreamLength) {
if (this.tokenStreamPointer >= this.tokenStreamLength) {
return null;
}
return tokenStream.get(tokenStreamPointer++);
return this.tokenStream.get(this.tokenStreamPointer++);
}
private Token peekToken() {
if (tokenStreamPointer>=tokenStreamLength) {
if (this.tokenStreamPointer >= this.tokenStreamLength) {
return null;
}
return tokenStream.get(tokenStreamPointer);
return this.tokenStream.get(this.tokenStreamPointer);
}
private void raiseInternalException(int pos, SpelMessage message,Object... inserts) {
throw new InternalParseException(new SpelParseException(expressionString,pos,message,inserts));
private void raiseInternalException(int pos, SpelMessage message, Object... inserts) {
throw new InternalParseException(new SpelParseException(this.expressionString,
pos, message, inserts));
}
public String toString(Token t) {
if (t.getKind().hasPayload()) {
return t.stringValue();
} else {
return t.kind.toString().toLowerCase();
}
return t.kind.toString().toLowerCase();
}
private void checkOperands(Token token, SpelNodeImpl left, SpelNodeImpl right) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,10 +30,10 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
/**
* A {@code SpelExpression} represents a parsed (valid) expression that is ready
* to be evaluated in a specified context. An expression can be evaluated
* standalone or in a specified context. During expression evaluation the context
* may be asked to resolve references to types, beans, properties, and methods.
* A {@code SpelExpression} represents a parsed (valid) expression that is ready to be
* evaluated in a specified context. An expression can be evaluated standalone or in a
* specified context. During expression evaluation the context may be asked to resolve
* references to types, beans, properties, and methods.
*
* @author Andy Clement
* @since 3.0
@ -64,51 +64,51 @@ public class SpelExpression implements Expression {
@Override
public Object getValue() throws EvaluationException {
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), configuration);
return ast.getValue(expressionState);
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
return this.ast.getValue(expressionState);
}
@Override
public Object getValue(Object rootObject) throws EvaluationException {
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
return ast.getValue(expressionState);
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration);
return this.ast.getValue(expressionState);
}
@Override
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), configuration);
TypedValue typedResultValue = ast.getTypedValue(expressionState);
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
TypedValue typedResultValue = this.ast.getTypedValue(expressionState);
return ExpressionUtils.convertTypedValue(expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
}
@Override
public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws EvaluationException {
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
TypedValue typedResultValue = ast.getTypedValue(expressionState);
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration);
TypedValue typedResultValue = this.ast.getTypedValue(expressionState);
return ExpressionUtils.convertTypedValue(expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
}
@Override
public Object getValue(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
return ast.getValue(new ExpressionState(context, configuration));
return this.ast.getValue(new ExpressionState(context, this.configuration));
}
@Override
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
return ast.getValue(new ExpressionState(context, toTypedValue(rootObject), configuration));
return this.ast.getValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration));
}
@Override
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
TypedValue typedResultValue = ast.getTypedValue(new ExpressionState(context, configuration));
TypedValue typedResultValue = this.ast.getTypedValue(new ExpressionState(context, this.configuration));
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
}
@Override
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType) throws EvaluationException {
TypedValue typedResultValue = ast.getTypedValue(new ExpressionState(context, toTypedValue(rootObject), configuration));
TypedValue typedResultValue = this.ast.getTypedValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration));
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
}
@ -125,15 +125,15 @@ public class SpelExpression implements Expression {
@Override
public Class<?> getValueType(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
ExpressionState eState = new ExpressionState(context, configuration);
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
ExpressionState eState = new ExpressionState(context, this.configuration);
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor != null ? typeDescriptor.getType() : null;
}
@Override
public Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration);
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), this.configuration);
TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor != null ? typeDescriptor.getType() : null;
}
@ -144,61 +144,61 @@ public class SpelExpression implements Expression {
@Override
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
ExpressionState eState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
return ast.getValueInternal(eState).getTypeDescriptor();
ExpressionState eState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration);
return this.ast.getValueInternal(eState).getTypeDescriptor();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
ExpressionState eState = new ExpressionState(context, configuration);
return ast.getValueInternal(eState).getTypeDescriptor();
ExpressionState eState = new ExpressionState(context, this.configuration);
return this.ast.getValueInternal(eState).getTypeDescriptor();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration);
return ast.getValueInternal(eState).getTypeDescriptor();
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), this.configuration);
return this.ast.getValueInternal(eState).getTypeDescriptor();
}
@Override
public String getExpressionString() {
return expression;
return this.expression;
}
@Override
public boolean isWritable(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
return ast.isWritable(new ExpressionState(context, configuration));
return this.ast.isWritable(new ExpressionState(context, this.configuration));
}
@Override
public boolean isWritable(Object rootObject) throws EvaluationException {
return ast.isWritable(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration));
return this.ast.isWritable(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration));
}
@Override
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
return ast.isWritable(new ExpressionState(context, toTypedValue(rootObject), configuration));
return this.ast.isWritable(new ExpressionState(context, toTypedValue(rootObject), this.configuration));
}
@Override
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
ast.setValue(new ExpressionState(context, configuration), value);
this.ast.setValue(new ExpressionState(context, this.configuration), value);
}
@Override
public void setValue(Object rootObject, Object value) throws EvaluationException {
ast.setValue(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration), value);
this.ast.setValue(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration), value);
}
@Override
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
ast.setValue(new ExpressionState(context, toTypedValue(rootObject), configuration), value);
this.ast.setValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration), value);
}
// impl only
@ -207,7 +207,7 @@ public class SpelExpression implements Expression {
* @return return the Abstract Syntax Tree for the expression
*/
public SpelNode getAST() {
return ast;
return this.ast;
}
/**
@ -217,7 +217,7 @@ public class SpelExpression implements Expression {
* @return the string representation of the AST
*/
public String toStringAST() {
return ast.toStringAST();
return this.ast.toStringAST();
}
/**
@ -225,10 +225,10 @@ public class SpelExpression implements Expression {
* @return the default evaluation context
*/
public EvaluationContext getEvaluationContext() {
if (defaultContext == null) {
defaultContext = new StandardEvaluationContext();
if (this.defaultContext == null) {
this.defaultContext = new StandardEvaluationContext();
}
return defaultContext;
return this.defaultContext;
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,7 +17,8 @@
package org.springframework.expression.spel.standard;
/**
* Holder for a kind of token, the associated data and its position in the input data stream (start/end).
* Holder for a kind of token, the associated data and its position in the input data
* stream (start/end).
*
* @author Andy Clement
* @since 3.0
@ -25,12 +26,17 @@ package org.springframework.expression.spel.standard;
class Token {
TokenKind kind;
String data;
int startpos; // index of first character
int endpos; // index of char after the last character
/**
* Constructor for use when there is no particular data for the token (eg. TRUE or '+')
* Constructor for use when there is no particular data for the token (eg. TRUE or
* '+')
* @param startpos the exact start
* @param endpos the index to the last character
*/
@ -47,42 +53,42 @@ class Token {
public TokenKind getKind() {
return kind;
return this.kind;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("[").append(kind.toString());
if (kind.hasPayload()) {
s.append(":").append(data);
s.append("[").append(this.kind.toString());
if (this.kind.hasPayload()) {
s.append(":").append(this.data);
}
s.append("]");
s.append("(").append(startpos).append(",").append(endpos).append(")");
s.append("(").append(this.startpos).append(",").append(this.endpos).append(")");
return s.toString();
}
public boolean isIdentifier() {
return kind==TokenKind.IDENTIFIER;
return this.kind==TokenKind.IDENTIFIER;
}
public boolean isNumericRelationalOperator() {
return kind==TokenKind.GT || kind==TokenKind.GE || kind==TokenKind.LT || kind==TokenKind.LE || kind==TokenKind.EQ || kind==TokenKind.NE;
return this.kind==TokenKind.GT || this.kind==TokenKind.GE || this.kind==TokenKind.LT || this.kind==TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE;
}
public String stringValue() {
return data;
return this.data;
}
public Token asInstanceOfToken() {
return new Token(TokenKind.INSTANCEOF,startpos,endpos);
return new Token(TokenKind.INSTANCEOF,this.startpos,this.endpos);
}
public Token asMatchesToken() {
return new Token(TokenKind.MATCHES,startpos,endpos);
return new Token(TokenKind.MATCHES,this.startpos,this.endpos);
}
public Token asBetweenToken() {
return new Token(TokenKind.BETWEEN,startpos,endpos);
return new Token(TokenKind.BETWEEN,this.startpos,this.endpos);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,44 +17,135 @@
package org.springframework.expression.spel.standard;
/**
* Token Kinds.
*
* @author Andy Clement
* @since 3.0
*/
enum TokenKind {
// ordered by priority - operands first
LITERAL_INT, LITERAL_LONG, LITERAL_HEXINT, LITERAL_HEXLONG, LITERAL_STRING, LITERAL_REAL, LITERAL_REAL_FLOAT,
LPAREN("("), RPAREN(")"), COMMA(","), IDENTIFIER,
COLON(":"),HASH("#"),RSQUARE("]"), LSQUARE("["),
LCURLY("{"),RCURLY("}"),
DOT("."), PLUS("+"), STAR("*"), MINUS("-"), SELECT_FIRST("^["), SELECT_LAST("$["), QMARK("?"), PROJECT("!["),
DIV("/"), GE(">="), GT(">"), LE("<="), LT("<"), EQ("=="), NE("!="),
MOD("%"), NOT("!"), ASSIGN("="), INSTANCEOF("instanceof"), MATCHES("matches"), BETWEEN("between"),
SELECT("?["), POWER("^"),
ELVIS("?:"), SAFE_NAVI("?."), BEAN_REF("@"), SYMBOLIC_OR("||"), SYMBOLIC_AND("&&"), INC("++"), DEC("--")
;
LITERAL_INT,
LITERAL_LONG,
LITERAL_HEXINT,
LITERAL_HEXLONG,
LITERAL_STRING,
LITERAL_REAL,
LITERAL_REAL_FLOAT,
LPAREN("("),
RPAREN(")"),
COMMA(","),
IDENTIFIER,
COLON(":"),
HASH("#"),
RSQUARE("]"),
LSQUARE("["),
LCURLY("{"),
RCURLY("}"),
DOT("."),
PLUS("+"),
STAR("*"),
MINUS("-"),
SELECT_FIRST("^["),
SELECT_LAST("$["),
QMARK("?"),
PROJECT("!["),
DIV("/"),
GE(">="),
GT(">"),
LE("<="),
LT("<"),
EQ("=="),
NE("!="),
MOD("%"),
NOT("!"),
ASSIGN("="),
INSTANCEOF("instanceof"),
MATCHES("matches"),
BETWEEN("between"),
SELECT("?["),
POWER("^"),
ELVIS("?:"),
SAFE_NAVI("?."),
BEAN_REF("@"),
SYMBOLIC_OR("||"),
SYMBOLIC_AND("&&"),
INC("++"),
DEC("--");
char[] tokenChars;
private boolean hasPayload; // is there more to this token than simply the kind
private TokenKind(String tokenString) {
tokenChars = tokenString.toCharArray();
hasPayload = tokenChars.length==0;
this.tokenChars = tokenString.toCharArray();
this.hasPayload = this.tokenChars.length==0;
}
private TokenKind() {
this("");
}
@Override
public String toString() {
return this.name()+(tokenChars.length!=0?"("+new String(tokenChars)+")":"");
return this.name()+(this.tokenChars.length!=0?"("+new String(this.tokenChars)+")":"");
}
public boolean hasPayload() {
return hasPayload;
return this.hasPayload;
}
public int getLength() {
return tokenChars.length;
return this.tokenChars.length;
}
}

View File

@ -34,276 +34,337 @@ import org.springframework.util.Assert;
*/
class Tokenizer {
// if this is changed, it must remain sorted
private static final String[] ALTERNATIVE_OPERATOR_NAMES = { "DIV", "EQ", "GE", "GT",
"LE", "LT", "MOD", "NE", "NOT" };
private static final byte FLAGS[] = new byte[256];
private static final byte IS_DIGIT = 0x01;
private static final byte IS_HEXDIGIT = 0x02;
private static final byte IS_ALPHA = 0x04;
static {
for (int ch = '0'; ch <= '9'; ch++) {
FLAGS[ch] |= IS_DIGIT | IS_HEXDIGIT;
}
for (int ch = 'A'; ch <= 'F'; ch++) {
FLAGS[ch] |= IS_HEXDIGIT;
}
for (int ch = 'a'; ch <= 'f'; ch++) {
FLAGS[ch] |= IS_HEXDIGIT;
}
for (int ch = 'A'; ch <= 'Z'; ch++) {
FLAGS[ch] |= IS_ALPHA;
}
for (int ch = 'a'; ch <= 'z'; ch++) {
FLAGS[ch] |= IS_ALPHA;
}
}
String expressionString;
char[] toProcess;
int pos;
int max;
List<Token> tokens = new ArrayList<Token>();
public Tokenizer(String inputdata) {
this.expressionString = inputdata;
this.toProcess = (inputdata+"\0").toCharArray();
this.max = toProcess.length;
this.toProcess = (inputdata + "\0").toCharArray();
this.max = this.toProcess.length;
this.pos = 0;
process();
}
public void process() {
while (pos<max) {
char ch = toProcess[pos];
while (this.pos < this.max) {
char ch = this.toProcess[this.pos];
if (isAlphabetic(ch)) {
lexIdentifier();
} else {
}
else {
switch (ch) {
case '+':
if (isTwoCharToken(TokenKind.INC)) {
pushPairToken(TokenKind.INC);
} else {
pushCharToken(TokenKind.PLUS);
}
break;
case '_': // the other way to start an identifier
lexIdentifier();
break;
case '-':
if (isTwoCharToken(TokenKind.DEC)) {
pushPairToken(TokenKind.DEC);
} else {
pushCharToken(TokenKind.MINUS);
}
break;
case ':':
pushCharToken(TokenKind.COLON);
break;
case '.':
pushCharToken(TokenKind.DOT);
break;
case ',':
pushCharToken(TokenKind.COMMA);
break;
case '*':
pushCharToken(TokenKind.STAR);
break;
case '/':
pushCharToken(TokenKind.DIV);
break;
case '%':
pushCharToken(TokenKind.MOD);
break;
case '(':
pushCharToken(TokenKind.LPAREN);
break;
case ')':
pushCharToken(TokenKind.RPAREN);
break;
case '[':
pushCharToken(TokenKind.LSQUARE);
break;
case '#':
pushCharToken(TokenKind.HASH);
break;
case ']':
pushCharToken(TokenKind.RSQUARE);
break;
case '{':
pushCharToken(TokenKind.LCURLY);
break;
case '}':
pushCharToken(TokenKind.RCURLY);
break;
case '@':
pushCharToken(TokenKind.BEAN_REF);
break;
case '^':
if (isTwoCharToken(TokenKind.SELECT_FIRST)) {
pushPairToken(TokenKind.SELECT_FIRST);
} else {
pushCharToken(TokenKind.POWER);
}
break;
case '!':
if (isTwoCharToken(TokenKind.NE)) {
pushPairToken(TokenKind.NE);
} else if (isTwoCharToken(TokenKind.PROJECT)) {
pushPairToken(TokenKind.PROJECT);
} else {
pushCharToken(TokenKind.NOT);
}
break;
case '=':
if (isTwoCharToken(TokenKind.EQ)) {
pushPairToken(TokenKind.EQ);
} else {
pushCharToken(TokenKind.ASSIGN);
}
break;
case '&':
if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) {
throw new InternalParseException(new SpelParseException(
expressionString, pos,
SpelMessage.MISSING_CHARACTER, "&"));
}
pushPairToken(TokenKind.SYMBOLIC_AND);
break;
case '|':
if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) {
throw new InternalParseException(new SpelParseException(
expressionString, pos,
SpelMessage.MISSING_CHARACTER, "|"));
}
pushPairToken(TokenKind.SYMBOLIC_OR);
break;
case '?':
if (isTwoCharToken(TokenKind.SELECT)) {
pushPairToken(TokenKind.SELECT);
} else if (isTwoCharToken(TokenKind.ELVIS)) {
pushPairToken(TokenKind.ELVIS);
} else if (isTwoCharToken(TokenKind.SAFE_NAVI)) {
pushPairToken(TokenKind.SAFE_NAVI);
} else {
pushCharToken(TokenKind.QMARK);
}
break;
case '$':
if (isTwoCharToken(TokenKind.SELECT_LAST)) {
pushPairToken(TokenKind.SELECT_LAST);
} else {
case '+':
if (isTwoCharToken(TokenKind.INC)) {
pushPairToken(TokenKind.INC);
}
else {
pushCharToken(TokenKind.PLUS);
}
break;
case '_': // the other way to start an identifier
lexIdentifier();
}
break;
case '>':
if (isTwoCharToken(TokenKind.GE)) {
pushPairToken(TokenKind.GE);
} else {
pushCharToken(TokenKind.GT);
}
break;
case '<':
if (isTwoCharToken(TokenKind.LE)) {
pushPairToken(TokenKind.LE);
} else {
pushCharToken(TokenKind.LT);
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
lexNumericLiteral(ch=='0');
break;
case ' ':
case '\t':
case '\r':
case '\n':
// drift over white space
pos++;
break;
case '\'':
lexQuotedStringLiteral();
break;
case '"':
lexDoubleQuotedStringLiteral();
break;
case 0:
// hit sentinel at end of value
pos++; // will take us to the end
break;
case '\\':
throw new InternalParseException(new SpelParseException(expressionString,pos,SpelMessage.UNEXPECTED_ESCAPE_CHAR));
default:
throw new IllegalStateException("Cannot handle ("+Integer.valueOf(ch)+") '"+ch+"'");
break;
case '-':
if (isTwoCharToken(TokenKind.DEC)) {
pushPairToken(TokenKind.DEC);
}
else {
pushCharToken(TokenKind.MINUS);
}
break;
case ':':
pushCharToken(TokenKind.COLON);
break;
case '.':
pushCharToken(TokenKind.DOT);
break;
case ',':
pushCharToken(TokenKind.COMMA);
break;
case '*':
pushCharToken(TokenKind.STAR);
break;
case '/':
pushCharToken(TokenKind.DIV);
break;
case '%':
pushCharToken(TokenKind.MOD);
break;
case '(':
pushCharToken(TokenKind.LPAREN);
break;
case ')':
pushCharToken(TokenKind.RPAREN);
break;
case '[':
pushCharToken(TokenKind.LSQUARE);
break;
case '#':
pushCharToken(TokenKind.HASH);
break;
case ']':
pushCharToken(TokenKind.RSQUARE);
break;
case '{':
pushCharToken(TokenKind.LCURLY);
break;
case '}':
pushCharToken(TokenKind.RCURLY);
break;
case '@':
pushCharToken(TokenKind.BEAN_REF);
break;
case '^':
if (isTwoCharToken(TokenKind.SELECT_FIRST)) {
pushPairToken(TokenKind.SELECT_FIRST);
}
else {
pushCharToken(TokenKind.POWER);
}
break;
case '!':
if (isTwoCharToken(TokenKind.NE)) {
pushPairToken(TokenKind.NE);
}
else if (isTwoCharToken(TokenKind.PROJECT)) {
pushPairToken(TokenKind.PROJECT);
}
else {
pushCharToken(TokenKind.NOT);
}
break;
case '=':
if (isTwoCharToken(TokenKind.EQ)) {
pushPairToken(TokenKind.EQ);
}
else {
pushCharToken(TokenKind.ASSIGN);
}
break;
case '&':
if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) {
throw new InternalParseException(new SpelParseException(
this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER,
"&"));
}
pushPairToken(TokenKind.SYMBOLIC_AND);
break;
case '|':
if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) {
throw new InternalParseException(new SpelParseException(
this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER,
"|"));
}
pushPairToken(TokenKind.SYMBOLIC_OR);
break;
case '?':
if (isTwoCharToken(TokenKind.SELECT)) {
pushPairToken(TokenKind.SELECT);
}
else if (isTwoCharToken(TokenKind.ELVIS)) {
pushPairToken(TokenKind.ELVIS);
}
else if (isTwoCharToken(TokenKind.SAFE_NAVI)) {
pushPairToken(TokenKind.SAFE_NAVI);
}
else {
pushCharToken(TokenKind.QMARK);
}
break;
case '$':
if (isTwoCharToken(TokenKind.SELECT_LAST)) {
pushPairToken(TokenKind.SELECT_LAST);
}
else {
lexIdentifier();
}
break;
case '>':
if (isTwoCharToken(TokenKind.GE)) {
pushPairToken(TokenKind.GE);
}
else {
pushCharToken(TokenKind.GT);
}
break;
case '<':
if (isTwoCharToken(TokenKind.LE)) {
pushPairToken(TokenKind.LE);
}
else {
pushCharToken(TokenKind.LT);
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
lexNumericLiteral(ch == '0');
break;
case ' ':
case '\t':
case '\r':
case '\n':
// drift over white space
this.pos++;
break;
case '\'':
lexQuotedStringLiteral();
break;
case '"':
lexDoubleQuotedStringLiteral();
break;
case 0:
// hit sentinel at end of value
this.pos++; // will take us to the end
break;
case '\\':
throw new InternalParseException(
new SpelParseException(this.expressionString, this.pos,
SpelMessage.UNEXPECTED_ESCAPE_CHAR));
default:
throw new IllegalStateException("Cannot handle ("
+ Integer.valueOf(ch) + ") '" + ch + "'");
}
}
}
}
public List<Token> getTokens() {
return tokens;
return this.tokens;
}
// STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
private void lexQuotedStringLiteral() {
int start = pos;
int start = this.pos;
boolean terminated = false;
while (!terminated) {
pos++;
char ch = toProcess[pos];
if (ch=='\'') {
this.pos++;
char ch = this.toProcess[this.pos];
if (ch == '\'') {
// may not be the end if the char after is also a '
if (toProcess[pos+1]=='\'') {
pos++; // skip over that too, and continue
} else {
if (this.toProcess[this.pos + 1] == '\'') {
this.pos++; // skip over that too, and continue
}
else {
terminated = true;
}
}
if (ch==0) {
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NON_TERMINATING_QUOTED_STRING));
if (ch == 0) {
throw new InternalParseException(new SpelParseException(this.expressionString,
start, SpelMessage.NON_TERMINATING_QUOTED_STRING));
}
}
pos++;
tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, pos));
this.pos++;
this.tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start, this.pos), start, this.pos));
}
// DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
// DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
private void lexDoubleQuotedStringLiteral() {
int start = pos;
int start = this.pos;
boolean terminated = false;
while (!terminated) {
pos++;
char ch = toProcess[pos];
if (ch=='"') {
this.pos++;
char ch = this.toProcess[this.pos];
if (ch == '"') {
// may not be the end if the char after is also a "
if (toProcess[pos+1]=='"') {
pos++; // skip over that too, and continue
} else {
if (this.toProcess[this.pos + 1] == '"') {
this.pos++; // skip over that too, and continue
}
else {
terminated = true;
}
}
if (ch==0) {
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING));
if (ch == 0) {
throw new InternalParseException(new SpelParseException(this.expressionString,
start, SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING));
}
}
pos++;
tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, pos));
this.pos++;
this.tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start, this.pos), start, this.pos));
}
// REAL_LITERAL :
// ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
// ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
// ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
// ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
// fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
// fragment HEX_DIGIT : '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';
//
// fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* (DECIMAL_DIGIT)+ ;
// fragment SIGN : '+' | '-' ;
// fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';
// INTEGER_LITERAL
// : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
// REAL_LITERAL :
// ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
// ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
// ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
// ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
// fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
// fragment HEX_DIGIT :
// '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';
//
// fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)*
// (DECIMAL_DIGIT)+ ;
// fragment SIGN : '+' | '-' ;
// fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';
// INTEGER_LITERAL
// : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
private void lexNumericLiteral(boolean firstCharIsZero) {
boolean isReal = false;
int start = pos;
char ch = toProcess[pos+1];
boolean isHex = ch=='x' || ch=='X';
int start = this.pos;
char ch = this.toProcess[this.pos + 1];
boolean isHex = ch == 'x' || ch == 'X';
// deal with hexadecimal
if (firstCharIsZero && isHex) {
pos=pos+1;
this.pos = this.pos + 1;
do {
pos++;
} while (isHexadecimalDigit(toProcess[pos]));
if (isChar('L','l')) {
pushHexIntToken(subarray(start+2,pos),true, start, pos);
pos++;
} else {
pushHexIntToken(subarray(start+2,pos),false, start, pos);
this.pos++;
}
while (isHexadecimalDigit(this.toProcess[this.pos]));
if (isChar('L', 'l')) {
pushHexIntToken(subarray(start + 2, this.pos), true, start, this.pos);
this.pos++;
}
else {
pushHexIntToken(subarray(start + 2, this.pos), false, start, this.pos);
}
return;
}
@ -312,134 +373,150 @@ class Tokenizer {
// Consume first part of number
do {
pos++;
} while (isDigit(toProcess[pos]));
this.pos++;
}
while (isDigit(this.toProcess[this.pos]));
// a '.' indicates this number is a real
ch = toProcess[pos];
if (ch=='.') {
ch = this.toProcess[this.pos];
if (ch == '.') {
isReal = true;
int dotpos = pos;
int dotpos = this.pos;
// carry on consuming digits
do {
pos++;
} while (isDigit(toProcess[pos]));
if (pos == dotpos + 1) {
this.pos++;
}
while (isDigit(this.toProcess[this.pos]));
if (this.pos == dotpos + 1) {
// the number is something like '3.'. It is really an int but may be
// part of something like '3.toString()'. In this case process it as
// an int and leave the dot as a separate token.
pos = dotpos;
pushIntToken(subarray(start, pos), false, start, pos);
this.pos = dotpos;
pushIntToken(subarray(start, this.pos), false, start, this.pos);
return;
}
}
int endOfNumber = pos;
int endOfNumber = this.pos;
// Now there may or may not be an exponent
// is it a long ?
if (isChar('L','l')) {
if (isChar('L', 'l')) {
if (isReal) { // 3.4L - not allowed
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.REAL_CANNOT_BE_LONG));
throw new InternalParseException(new SpelParseException(this.expressionString,
start, SpelMessage.REAL_CANNOT_BE_LONG));
}
pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber);
pos++;
} else if (isExponentChar(toProcess[pos])) {
this.pos++;
}
else if (isExponentChar(this.toProcess[this.pos])) {
isReal = true; // if it wasn't before, it is now
pos++;
char possibleSign = toProcess[pos];
this.pos++;
char possibleSign = this.toProcess[this.pos];
if (isSign(possibleSign)) {
pos++;
this.pos++;
}
// exponent digits
do {
pos++;
} while (isDigit(toProcess[pos]));
boolean isFloat = false;
if (isFloatSuffix(toProcess[pos])) {
isFloat = true;
endOfNumber = ++pos;
} else if (isDoubleSuffix(toProcess[pos])) {
endOfNumber = ++pos;
this.pos++;
}
pushRealToken(subarray(start,pos), isFloat, start, pos);
} else {
ch = toProcess[pos];
while (isDigit(this.toProcess[this.pos]));
boolean isFloat = false;
if (isFloatSuffix(this.toProcess[this.pos])) {
isFloat = true;
endOfNumber = ++this.pos;
}
else if (isDoubleSuffix(this.toProcess[this.pos])) {
endOfNumber = ++this.pos;
}
pushRealToken(subarray(start, this.pos), isFloat, start, this.pos);
}
else {
ch = this.toProcess[this.pos];
boolean isFloat = false;
if (isFloatSuffix(ch)) {
isReal = true;
isFloat = true;
endOfNumber = ++pos;
} else if (isDoubleSuffix(ch)) {
endOfNumber = ++this.pos;
}
else if (isDoubleSuffix(ch)) {
isReal = true;
endOfNumber = ++pos;
endOfNumber = ++this.pos;
}
if (isReal) {
pushRealToken(subarray(start,endOfNumber), isFloat, start, endOfNumber);
} else {
pushIntToken(subarray(start,endOfNumber), false, start, endOfNumber);
pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber);
}
else {
pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber);
}
}
}
// if this is changed, it must remain sorted
private static final String[] alternativeOperatorNames = { "DIV","EQ","GE","GT","LE","LT","MOD","NE","NOT"};
private void lexIdentifier() {
int start = pos;
int start = this.pos;
do {
pos++;
} while (isIdentifier(toProcess[pos]));
char[] subarray = subarray(start,pos);
this.pos++;
}
while (isIdentifier(this.toProcess[this.pos]));
char[] subarray = subarray(start, this.pos);
// Check if this is the alternative (textual) representation of an operator (see alternativeOperatorNames)
if ((pos-start)==2 || (pos-start)==3) {
// Check if this is the alternative (textual) representation of an operator (see
// alternativeOperatorNames)
if ((this.pos - start) == 2 || (this.pos - start) == 3) {
String asString = new String(subarray).toUpperCase();
int idx = Arrays.binarySearch(alternativeOperatorNames,asString);
if (idx>=0) {
pushOneCharOrTwoCharToken(TokenKind.valueOf(asString),start,subarray);
int idx = Arrays.binarySearch(ALTERNATIVE_OPERATOR_NAMES, asString);
if (idx >= 0) {
pushOneCharOrTwoCharToken(TokenKind.valueOf(asString), start, subarray);
return;
}
}
tokens.add(new Token(TokenKind.IDENTIFIER,subarray,start,pos));
this.tokens.add(new Token(TokenKind.IDENTIFIER, subarray, start, this.pos));
}
private void pushIntToken(char[] data,boolean isLong, int start, int end) {
private void pushIntToken(char[] data, boolean isLong, int start, int end) {
if (isLong) {
tokens.add(new Token(TokenKind.LITERAL_LONG,data, start, end));
} else {
tokens.add(new Token(TokenKind.LITERAL_INT,data, start, end));
this.tokens.add(new Token(TokenKind.LITERAL_LONG, data, start, end));
}
else {
this.tokens.add(new Token(TokenKind.LITERAL_INT, data, start, end));
}
}
private void pushHexIntToken(char[] data,boolean isLong, int start, int end) {
if (data.length==0) {
private void pushHexIntToken(char[] data, boolean isLong, int start, int end) {
if (data.length == 0) {
if (isLong) {
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NOT_A_LONG,expressionString.substring(start,end+1)));
} else {
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NOT_AN_INTEGER,expressionString.substring(start,end)));
throw new InternalParseException(new SpelParseException(this.expressionString,
start, SpelMessage.NOT_A_LONG, this.expressionString.substring(start,
end + 1)));
}
else {
throw new InternalParseException(new SpelParseException(this.expressionString,
start, SpelMessage.NOT_AN_INTEGER, this.expressionString.substring(
start, end)));
}
}
if (isLong) {
tokens.add(new Token(TokenKind.LITERAL_HEXLONG, data, start, end));
} else {
tokens.add(new Token(TokenKind.LITERAL_HEXINT, data, start, end));
this.tokens.add(new Token(TokenKind.LITERAL_HEXLONG, data, start, end));
}
else {
this.tokens.add(new Token(TokenKind.LITERAL_HEXINT, data, start, end));
}
}
private void pushRealToken(char[] data, boolean isFloat, int start, int end) {
if (isFloat) {
tokens.add(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end));
} else {
tokens.add(new Token(TokenKind.LITERAL_REAL, data, start, end));
this.tokens.add(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end));
}
else {
this.tokens.add(new Token(TokenKind.LITERAL_REAL, data, start, end));
}
}
private char[] subarray(int start, int end) {
char[] result = new char[end - start];
System.arraycopy(toProcess, start, result, 0, end - start);
System.arraycopy(this.toProcess, start, result, 0, end - start);
return result;
}
@ -448,98 +525,74 @@ class Tokenizer {
*/
private boolean isTwoCharToken(TokenKind kind) {
Assert.isTrue(kind.tokenChars.length == 2);
Assert.isTrue(toProcess[pos] == kind.tokenChars[0]);
return toProcess[pos+1] == kind.tokenChars[1];
Assert.isTrue(this.toProcess[this.pos] == kind.tokenChars[0]);
return this.toProcess[this.pos + 1] == kind.tokenChars[1];
}
/**
* Push a token of just one character in length.
*/
private void pushCharToken(TokenKind kind) {
tokens.add(new Token(kind,pos,pos+1));
pos++;
this.tokens.add(new Token(kind, this.pos, this.pos + 1));
this.pos++;
}
/**
* Push a token of two characters in length.
*/
private void pushPairToken(TokenKind kind) {
tokens.add(new Token(kind,pos,pos+2));
pos+=2;
this.tokens.add(new Token(kind, this.pos, this.pos + 2));
this.pos += 2;
}
private void pushOneCharOrTwoCharToken(TokenKind kind, int pos, char[] data) {
tokens.add(new Token(kind,data,pos,pos+kind.getLength()));
this.tokens.add(new Token(kind, data, pos, pos + kind.getLength()));
}
// ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*;
// ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*;
private boolean isIdentifier(char ch) {
return isAlphabetic(ch) || isDigit(ch) || ch=='_' || ch=='$';
return isAlphabetic(ch) || isDigit(ch) || ch == '_' || ch == '$';
}
private boolean isChar(char a,char b) {
char ch = toProcess[pos];
return ch==a || ch==b;
private boolean isChar(char a, char b) {
char ch = this.toProcess[this.pos];
return ch == a || ch == b;
}
private boolean isExponentChar(char ch) {
return ch=='e' || ch=='E';
return ch == 'e' || ch == 'E';
}
private boolean isFloatSuffix(char ch) {
return ch=='f' || ch=='F';
return ch == 'f' || ch == 'F';
}
private boolean isDoubleSuffix(char ch) {
return ch=='d' || ch=='D';
return ch == 'd' || ch == 'D';
}
private boolean isSign(char ch) {
return ch=='+' || ch=='-';
return ch == '+' || ch == '-';
}
private boolean isDigit(char ch) {
if (ch>255) {
if (ch > 255) {
return false;
}
return (flags[ch] & IS_DIGIT)!=0;
return (FLAGS[ch] & IS_DIGIT) != 0;
}
private boolean isAlphabetic(char ch) {
if (ch>255) {
if (ch > 255) {
return false;
}
return (flags[ch] & IS_ALPHA)!=0;
return (FLAGS[ch] & IS_ALPHA) != 0;
}
private boolean isHexadecimalDigit(char ch) {
if (ch>255) {
if (ch > 255) {
return false;
}
return (flags[ch] & IS_HEXDIGIT)!=0;
return (FLAGS[ch] & IS_HEXDIGIT) != 0;
}
private static final byte flags[] = new byte[256];
private static final byte IS_DIGIT=0x01;
private static final byte IS_HEXDIGIT=0x02;
private static final byte IS_ALPHA=0x04;
static {
for (int ch='0';ch<='9';ch++) {
flags[ch]|=IS_DIGIT | IS_HEXDIGIT;
}
for (int ch='A';ch<='F';ch++) {
flags[ch]|= IS_HEXDIGIT;
}
for (int ch='a';ch<='f';ch++) {
flags[ch]|= IS_HEXDIGIT;
}
for (int ch='A';ch<='Z';ch++) {
flags[ch]|= IS_ALPHA;
}
for (int ch='a';ch<='z';ch++) {
flags[ch]|= IS_ALPHA;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -35,12 +35,7 @@ public class BooleanTypedValue extends TypedValue {
public static BooleanTypedValue forValue(boolean b) {
if (b) {
return TRUE;
}
else {
return FALSE;
}
return (b ? TRUE : FALSE);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -42,13 +42,15 @@ import org.springframework.util.MethodInvoker;
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.
* 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 suppliedArgTypes the array of types that are being supplied at the point of
* invocation
* @param typeConverter a registered type converter
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
* @return a MatchInfo object indicating what kind of match it was or null if it was
* not a match
*/
static ArgumentsMatchInfo compareArguments(
List<TypeDescriptor> expectedArgTypes, List<TypeDescriptor> suppliedArgTypes, TypeConverter typeConverter) {
@ -199,6 +201,7 @@ public class ReflectionHelper {
}
}
}
// If already confirmed it cannot be a match, then return
if (match == null) {
return null;
@ -381,57 +384,74 @@ public class ReflectionHelper {
// method the arguments should have been converted to the box form of the required type.
Class<?> componentType = requiredParameterTypes[parameterCount-1].getComponentType();
if (componentType.isPrimitive()) {
if (componentType==Integer.TYPE) {
int[] repackagedArguments = (int[]) Array.newInstance(componentType, arraySize);
if (componentType == Integer.TYPE) {
int[] repackagedArguments = (int[]) Array.newInstance(componentType,
arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Integer)args[parameterCount + i - 1]).intValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
} else if(componentType==Float.TYPE) {
float[] repackagedArguments = (float[]) Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Float)args[parameterCount + i - 1]).floatValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
} else if(componentType==Double.TYPE) {
double[] repackagedArguments = (double[]) Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Double)args[parameterCount + i - 1]).doubleValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
} else if(componentType==Short.TYPE) {
short[] repackagedArguments = (short[]) Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Short)args[parameterCount + i - 1]).shortValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
} else if(componentType==Character.TYPE) {
char[] repackagedArguments = (char[]) Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Character)args[parameterCount + i - 1]).charValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
} else if(componentType==Byte.TYPE) {
byte[] repackagedArguments = (byte[]) Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Byte)args[parameterCount + i - 1]).byteValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
} else if(componentType==Boolean.TYPE) {
boolean[] repackagedArguments = (boolean[]) Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Boolean)args[parameterCount + i - 1]).booleanValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
} else if(componentType==Long.TYPE) {
long[] repackagedArguments = (long[]) Array.newInstance(componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Long)args[parameterCount + i - 1]).longValue();
repackagedArguments[i] = ((Integer) args[parameterCount + i - 1]).intValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
} else {
Object[] repackagedArguments = (Object[]) Array.newInstance(componentType, arraySize);
else if (componentType == Float.TYPE) {
float[] repackagedArguments = (float[]) Array.newInstance(
componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Float) args[parameterCount + i - 1]).floatValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
else if (componentType == Double.TYPE) {
double[] repackagedArguments = (double[]) Array.newInstance(
componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Double) args[parameterCount + i - 1]).doubleValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
else if (componentType == Short.TYPE) {
short[] repackagedArguments = (short[]) Array.newInstance(
componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Short) args[parameterCount + i - 1]).shortValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
else if (componentType == Character.TYPE) {
char[] repackagedArguments = (char[]) Array.newInstance(
componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Character) args[parameterCount + i - 1]).charValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
else if (componentType == Byte.TYPE) {
byte[] repackagedArguments = (byte[]) Array.newInstance(
componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Byte) args[parameterCount + i - 1]).byteValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
else if (componentType == Boolean.TYPE) {
boolean[] repackagedArguments = (boolean[]) Array.newInstance(
componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Boolean) args[parameterCount + i - 1]).booleanValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
else if (componentType == Long.TYPE) {
long[] repackagedArguments = (long[]) Array.newInstance(
componentType, arraySize);
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = ((Long) args[parameterCount + i - 1]).longValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
}
else {
Object[] repackagedArguments = (Object[]) Array.newInstance(
componentType, arraySize);
// Copy all but the varargs arguments
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = args[parameterCount + i - 1];

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,7 +25,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.util.ReflectionUtils;
/**
* A simple ConstructorExecutor implementation that runs a constructor using reflective invocation.
* A simple ConstructorExecutor implementation that runs a constructor using reflective
* invocation.
*
* @author Andy Clement
* @author Juergen Hoeller

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,7 +32,8 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
/**
* A constructor resolver that uses reflection to locate the constructor that should be invoked
* A constructor resolver that uses reflection to locate the constructor that should be
* invoked
*
* @author Andy Clement
* @author Juergen Hoeller
@ -43,10 +44,12 @@ public class ReflectiveConstructorResolver implements ConstructorResolver {
/**
* 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.
* <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>
*/
@Override

View File

@ -42,8 +42,8 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
/**
* Reflection-based {@link MethodResolver} used by default in {@link StandardEvaluationContext}
* unless explicit method resolvers have been specified.
* Reflection-based {@link MethodResolver} used by default in
* {@link StandardEvaluationContext} unless explicit method resolvers have been specified.
*
* @author Andy Clement
* @author Juergen Hoeller
@ -53,8 +53,6 @@ import org.springframework.expression.spel.SpelMessage;
*/
public class ReflectiveMethodResolver implements MethodResolver {
private static Method[] NO_METHODS = new Method[0];
private Map<Class<?>, MethodFilter> filters = null;
// Using distance will ensure a more accurate match is discovered,
@ -78,6 +76,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
this.useDistance = useDistance;
}
/**
* Locate a method on a type. There are three kinds of match that might occur:
* <ol>

View File

@ -38,9 +38,9 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Simple PropertyAccessor 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).
* Simple PropertyAccessor that uses reflection to access properties for reading and
* writing. A property can be accessed if it is accessible as a field on the object or
* through a getter (if being read) or a setter (if being written).
*
* @author Andy Clement
* @author Juergen Hoeller

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -242,8 +242,8 @@ public class StandardEvaluationContext implements EvaluationContext {
*/
public void registerMethodFilter(Class<?> type, MethodFilter filter) throws IllegalStateException {
ensureMethodResolversInitialized();
if (reflectiveMethodResolver != null) {
reflectiveMethodResolver.registerMethodFilter(type, filter);
if (this.reflectiveMethodResolver != null) {
this.reflectiveMethodResolver.registerMethodFilter(type, filter);
} else {
throw new IllegalStateException("Method filter cannot be set as the reflective method resolver is not in use");
}
@ -272,7 +272,7 @@ public class StandardEvaluationContext implements EvaluationContext {
private synchronized void initializeMethodResolvers() {
if (this.methodResolvers == null) {
List<MethodResolver> defaultResolvers = new ArrayList<MethodResolver>();
defaultResolvers.add(reflectiveMethodResolver = new ReflectiveMethodResolver());
defaultResolvers.add(this.reflectiveMethodResolver = new ReflectiveMethodResolver());
this.methodResolvers = defaultResolvers;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,8 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
/**
* A simple basic TypeComparator implementation. It supports comparison of numbers and types implementing Comparable.
* A simple basic TypeComparator implementation. It supports comparison of numbers and
* types implementing Comparable.
*
* @author Andy Clement
* @author Juergen Hoeller
@ -35,7 +36,8 @@ public class StandardTypeComparator implements TypeComparator {
// If one is null, check if the other is
if (left == null) {
return right == null ? 0 : -1;
} else if (right == null) {
}
else if (right == null) {
return 1; // left cannot be null
}
@ -46,20 +48,24 @@ public class StandardTypeComparator implements TypeComparator {
if (leftNumber instanceof Double || rightNumber instanceof Double) {
double d1 = leftNumber.doubleValue();
double d2 = rightNumber.doubleValue();
return Double.compare(d1,d2);
} else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return Double.compare(d1, d2);
}
if (leftNumber instanceof Float || rightNumber instanceof Float) {
float f1 = leftNumber.floatValue();
float f2 = rightNumber.floatValue();
return Float.compare(f1,f2);
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return Float.compare(f1, f2);
}
if (leftNumber instanceof Long || rightNumber instanceof Long) {
Long l1 = leftNumber.longValue();
Long l2 = rightNumber.longValue();
return l1.compareTo(l2);
} else {
Integer i1 = leftNumber.intValue();
Integer i2 = rightNumber.intValue();
return i1.compareTo(i2);
}
Integer i1 = leftNumber.intValue();
Integer i2 = rightNumber.intValue();
return i1.compareTo(i2);
}
try {
@ -78,12 +84,15 @@ public class StandardTypeComparator implements TypeComparator {
if (left == null || right == null) {
return true;
}
if (left instanceof Number && right instanceof Number) {
return true;
}
if (left instanceof Comparable) {
return true;
}
return false;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,8 +27,8 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.Assert;
/**
* Default implementation of the {@link TypeConverter} interface,
* delegating to a core Spring {@link ConversionService}.
* Default implementation of the {@link TypeConverter} interface, delegating to a core
* Spring {@link ConversionService}.
*
* @author Juergen Hoeller
* @author Andy Clement

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,8 +27,9 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.ClassUtils;
/**
* A default implementation of a TypeLocator that uses the context classloader (or any classloader set upon it). It
* supports 'well known' packages so if a type cannot be found it will try the registered imports to locate it.
* A default implementation of a TypeLocator that uses the context classloader (or any
* classloader set upon it). It supports 'well known' packages so if a type cannot be
* found it will try the registered imports to locate it.
*
* @author Andy Clement
* @author Juergen Hoeller
@ -36,7 +37,7 @@ import org.springframework.util.ClassUtils;
*/
public class StandardTypeLocator implements TypeLocator {
private ClassLoader loader;
private final ClassLoader loader;
private final List<String> knownPackagePrefixes = new ArrayList<String>();