Rename modules {org.springframework.*=>spring-*}
This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.
Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example
$ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history up until the renaming event, where
$ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history for all changes to the file, before and after the
renaming.
See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
List of outstanding things to think about - turn into JIRAs once distilled to a core set of issues
|
||||
|
||||
High Importance
|
||||
|
||||
- In the resolver/executor model we cache executors. They are currently recorded in the AST and so if the user chooses to evaluate an expression
|
||||
in a different context then the stored executor may be incorrect. It may harmless 'fail' which would cause us to retrieve a new one, but
|
||||
can it do anything malicious? In which case we either need to forget them when the context changes or store them elsewhere. Should caching be
|
||||
something that can be switched on/off by the context? (shouldCacheExecutors() on the interface?)
|
||||
- Expression serialization needs supporting
|
||||
- expression basic interface and common package. Should LiteralExpression be settable? should getExpressionString return quoted value?
|
||||
|
||||
Low Importance
|
||||
|
||||
- For the ternary operator, should isWritable() return true/false depending on evaluating the condition and check isWritable() of whichever branch it
|
||||
would have taken? At the moment ternary expressions are just considered NOT writable.
|
||||
- Enhance type locator interface with direct support for register/unregister imports and ability to set class loader?
|
||||
- Should some of the common errors (like SpelMessages.TYPE_NOT_FOUND) be promoted to top level exceptions?
|
||||
- Expression comparison - is it necessary?
|
||||
|
||||
Syntax
|
||||
|
||||
- should the 'is' operator change to 'instanceof' ?
|
||||
- in this expression we hit the problem of not being able to write chars, since '' always means string:
|
||||
evaluate("new java.lang.String('hello').charAt(2).equals('l'.charAt(0))", true, Boolean.class);
|
||||
So 'l'.charAt(0) was required - wonder if we can build in a converter for a single length string to char?
|
||||
Can't do that as equals take Object and so we don't know to do a cast in order to pass a char into equals
|
||||
We certainly cannot do a cast (unless casts are added to the syntax). See MethodInvocationTest.testStringClass()
|
||||
- MATCHES is now the thing that takes a java regex. What does 'like' do? right now it is the SQL LIKE that supports
|
||||
wildcards % and _. It has a poor implementation but I need to know whether to keep it in the language before
|
||||
fixing that.
|
||||
- Need to agree on a standard date format for 'default' processing of dates. Currently it is:
|
||||
formatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.UK);
|
||||
// this is something of this format: "Wed, 4 Jul 2001 12:08:56 GMT"
|
||||
// http://java.sun.com/j2se/1.4.2/docs/api/java/text/SimpleDateFormat.html
|
||||
- See LiteralTests for Date (4,5,6) - should date take an expression rather than be hardcoded in the grammar
|
||||
to take 2 strings only?
|
||||
- when doing arithmetic, eg. 8.4 / 4 and the user asks for an Integer return type - do we silently coerce or
|
||||
say we cannot as it won't fit into an int? (see OperatorTests.testMathOperatorDivide04)
|
||||
- Is $index within projection/selection useful or just cute?
|
||||
- All reals are represented as Doubles (so 1.25f is held internally as a double, can be converted to float when required though) - is that ok?
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* An AccessException is thrown by an accessor if it has an unexpected problem.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class AccessException extends Exception {
|
||||
|
||||
/**
|
||||
* Create an AccessException with a specific message and cause.
|
||||
* @param message the message
|
||||
* @param cause the cause
|
||||
*/
|
||||
public AccessException(String message, Exception cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AccessException with a specific message.
|
||||
* @param message the message
|
||||
*/
|
||||
public AccessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* A bean resolver can be registered with the evaluation context
|
||||
* and will kick in for <code>@myBeanName</code> still expressions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public interface BeanResolver {
|
||||
|
||||
/**
|
||||
* Look up the named bean and return it.
|
||||
* @param context the current evaluation context
|
||||
* @param beanName the name of the bean to lookup
|
||||
* @return an object representing the bean
|
||||
* @throws AccessException if there is an unexpected problem resolving the named bean
|
||||
*/
|
||||
Object resolve(EvaluationContext context, String beanName) throws AccessException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
|
||||
// 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.
|
||||
*
|
||||
* They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go
|
||||
* back to the resolvers to ask for a new one.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface 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
|
||||
* @return the new object
|
||||
* @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;
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
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.
|
||||
*
|
||||
* @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</code> 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
|
||||
* @return a ConstructorExecutor that can invoke the constructor, or null if non found
|
||||
*/
|
||||
ConstructorExecutor resolve(EvaluationContext context, String typeName, List<TypeDescriptor> argumentTypes)
|
||||
throws AccessException;
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
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.
|
||||
*
|
||||
* <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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
TypedValue getRootObject();
|
||||
|
||||
/**
|
||||
* @return a list of resolvers that will be asked in turn to locate a constructor
|
||||
*/
|
||||
List<ConstructorResolver> getConstructorResolvers();
|
||||
|
||||
/**
|
||||
* @return a list of resolvers that will be asked in turn to locate a method
|
||||
*/
|
||||
List<MethodResolver> getMethodResolvers();
|
||||
|
||||
/**
|
||||
* @return a list of accessors that will be asked in turn to read/write a property
|
||||
*/
|
||||
List<PropertyAccessor> getPropertyAccessors();
|
||||
|
||||
/**
|
||||
* @return a type locator that can be used to find types, either by short or fully qualified name.
|
||||
*/
|
||||
TypeLocator getTypeLocator();
|
||||
|
||||
/**
|
||||
* @return a type converter that can convert (or coerce) a value from one type to another.
|
||||
*/
|
||||
TypeConverter getTypeConverter();
|
||||
|
||||
/**
|
||||
* @return a type comparator for comparing pairs of objects for equality.
|
||||
*/
|
||||
TypeComparator getTypeComparator();
|
||||
|
||||
/**
|
||||
* @return an operator overloader that may support mathematical operations
|
||||
* between more than the standard set of types
|
||||
*/
|
||||
OperatorOverloader getOperatorOverloader();
|
||||
|
||||
/**
|
||||
* @return a bean resolver that can look up beans by name
|
||||
*/
|
||||
BeanResolver getBeanResolver();
|
||||
|
||||
/**
|
||||
* Set a named variable within this evaluation context to a specified value.
|
||||
* @param name variable to set
|
||||
* @param value value to be placed in the variable
|
||||
*/
|
||||
void setVariable(String name, Object value);
|
||||
|
||||
/**
|
||||
* Look up a named variable within this evaluation context.
|
||||
* @param name variable to lookup
|
||||
* @return the value of the variable
|
||||
*/
|
||||
Object lookupVariable(String name);
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* Represent an exception that occurs during expression evaluation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class EvaluationException extends ExpressionException {
|
||||
|
||||
/**
|
||||
* Creates a new expression evaluation exception.
|
||||
* @param position the position in the expression where the problem occurred
|
||||
* @param message description of the problem that occurred
|
||||
*/
|
||||
public EvaluationException(int position, String message) {
|
||||
super(position, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression evaluation exception.
|
||||
* @param expressionString the expression that could not be evaluated
|
||||
* @param message description of the problem that occurred
|
||||
*/
|
||||
public EvaluationException(String expressionString, String message) {
|
||||
super(expressionString, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression evaluation exception.
|
||||
* @param position the position in the expression where the problem occurred
|
||||
* @param message description of the problem that occurred
|
||||
* @param cause the underlying cause of this exception
|
||||
*/
|
||||
public EvaluationException(int position, String message, Throwable cause) {
|
||||
super(position, message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression evaluation exception.
|
||||
* @param message description of the problem that occurred
|
||||
*/
|
||||
public EvaluationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public EvaluationException(String message, Throwable cause) {
|
||||
super(message,cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
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.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface Expression {
|
||||
|
||||
/**
|
||||
* Evaluate this expression in the default standard context.
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
Object getValue() throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Evaluate this expression against the specified root object
|
||||
* @param rootObject the root object against which properties/etc will be resolved
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
Object getValue(Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Evaluate the expression in the default context. If the result of the evaluation does not match (and
|
||||
* cannot be converted to) the expected result type then an exception will be returned.
|
||||
* @param desiredResultType the class the caller would like the result to be
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
<T> T getValue(Class<T> desiredResultType) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Evaluate the expression in the default context against the specified root object. If the
|
||||
* result of the evaluation does not match (and cannot be converted to) the expected result type
|
||||
* then an exception will be returned.
|
||||
* @param rootObject the root object against which properties/etc will be resolved
|
||||
* @param desiredResultType the class the caller would like the result to be
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
<T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Evaluate this expression in the provided context and return the result of evaluation.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
Object getValue(EvaluationContext context) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Evaluate this expression in the provided context and return the result of evaluation, but use
|
||||
* the supplied root context as an override for any default root object specified in the context.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @param rootObject the root object against which properties/etc will be resolved
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc -
|
||||
* the type of the evaluation result is expected to be of a particular class and an exception will be thrown if it
|
||||
* is not and cannot be converted to that type.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @param desiredResultType the class the caller would like the result to be
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
<T> T getValue(EvaluationContext context, Class<T> desiredResultType) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc -
|
||||
* the type of the evaluation result is expected to be of a particular class and an exception will be thrown if it
|
||||
* is not and cannot be converted to that type. The supplied root object overrides any default specified on the
|
||||
* supplied context.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @param rootObject the root object against which properties/etc will be resolved
|
||||
* @param desiredResultType the class the caller would like the result to be
|
||||
* @return the evaluation result
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
<T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
|
||||
* method using the default context.
|
||||
* @return the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
Class getValueType() throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
|
||||
* method using the default context.
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @return the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
Class getValueType(Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
|
||||
* method for the given context.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @return the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
Class getValueType(EvaluationContext context) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
|
||||
* method for the given context. The supplied root object overrides any specified in the context.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @return the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
|
||||
* method using the default context.
|
||||
* @return a type descriptor for the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
TypeDescriptor getValueTypeDescriptor() throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
|
||||
* method using the default context.
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @return a type descriptor for the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)}
|
||||
* method for the given context.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @return a type descriptor for the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
|
||||
* the given context. The supplied root object overrides any specified in the context.
|
||||
* @param context the context in which to evaluate the expression
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @return a type descriptor for the most general type of value that can be set on this context
|
||||
* @throws EvaluationException if there is a problem determining the type
|
||||
*/
|
||||
TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Determine if an expression can be written to, i.e. setValue() can be called.
|
||||
* @param context the context in which the expression should be checked
|
||||
* @return true if the expression is writable
|
||||
* @throws EvaluationException if there is a problem determining if it is writable
|
||||
*/
|
||||
boolean isWritable(EvaluationContext context) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Determine if an expression can be written to, i.e. setValue() can be called.
|
||||
* The supplied root object overrides any specified in the context.
|
||||
* @param context the context in which the expression should be checked
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @return true if the expression is writable
|
||||
* @throws EvaluationException if there is a problem determining if it is writable
|
||||
*/
|
||||
boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Determine if an expression can be written to, i.e. setValue() can be called.
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @return true if the expression is writable
|
||||
* @throws EvaluationException if there is a problem determining if it is writable
|
||||
*/
|
||||
boolean isWritable(Object rootObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Set this expression in the provided context to the value provided.
|
||||
*
|
||||
* @param context the context in which to set the value of the expression
|
||||
* @param value the new value
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
void setValue(EvaluationContext context, Object value) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Set this expression in the provided context to the value provided.
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @param value the new value
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
void setValue(Object rootObject, Object value) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Set this expression in the provided context to the value provided.
|
||||
* The supplied root object overrides any specified in the context.
|
||||
* @param context the context in which to set the value of the expression
|
||||
* @param rootObject the root object against which to evaluate the expression
|
||||
* @param value the new value
|
||||
* @throws EvaluationException if there is a problem during evaluation
|
||||
*/
|
||||
void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Returns the original string used to create this expression, unmodified.
|
||||
* @return the original expression string
|
||||
*/
|
||||
String getExpressionString();
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
|
||||
/**
|
||||
* Super class for exceptions that can occur whilst processing expressions
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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
|
||||
* @param message a descriptive message
|
||||
*/
|
||||
public ExpressionException(String expressionString, String message) {
|
||||
super(message);
|
||||
this.position = -1;
|
||||
this.expressionString = expressionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression exception.
|
||||
* @param expressionString the expression string
|
||||
* @param position the position in the expression string where the problem occurred
|
||||
* @param message a descriptive message
|
||||
*/
|
||||
public ExpressionException(String expressionString, int position, String message) {
|
||||
super(message);
|
||||
this.position = position;
|
||||
this.expressionString = expressionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression exception.
|
||||
* @param position the position in the expression string where the problem occurred
|
||||
* @param message a descriptive message
|
||||
*/
|
||||
public ExpressionException(int position, String message) {
|
||||
super(message);
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression exception.
|
||||
* @param position the position in the expression string where the problem occurred
|
||||
* @param message a descriptive message
|
||||
* @param cause the underlying cause of this exception
|
||||
*/
|
||||
public ExpressionException(int position, String message, Throwable cause) {
|
||||
super(message,cause);
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression exception.
|
||||
* @param message a descriptive message
|
||||
*/
|
||||
public ExpressionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ExpressionException(String message, Throwable cause) {
|
||||
super(message,cause);
|
||||
}
|
||||
|
||||
public String toDetailedString() {
|
||||
StringBuilder output = new StringBuilder();
|
||||
if (expressionString!=null) {
|
||||
output.append("Expression '");
|
||||
output.append(expressionString);
|
||||
output.append("'");
|
||||
if (position!=-1) {
|
||||
output.append(" @ ");
|
||||
output.append(position);
|
||||
}
|
||||
output.append(": ");
|
||||
}
|
||||
output.append(getMessage());
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
public final String getExpressionString() {
|
||||
return this.expressionString;
|
||||
}
|
||||
|
||||
public final int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public class ExpressionInvocationTargetException extends EvaluationException {
|
||||
|
||||
public ExpressionInvocationTargetException(int position, String message, Throwable cause) {
|
||||
super(position, message, cause);
|
||||
}
|
||||
|
||||
public ExpressionInvocationTargetException(int position, String message) {
|
||||
super(position, message);
|
||||
}
|
||||
|
||||
public ExpressionInvocationTargetException(String expressionString, String message) {
|
||||
super(expressionString, message);
|
||||
}
|
||||
|
||||
public ExpressionInvocationTargetException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ExpressionInvocationTargetException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* Parses expression strings into compiled expressions that can be evaluated.
|
||||
* Supports parsing templates as well as standard expression strings.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ExpressionParser {
|
||||
|
||||
/**
|
||||
* Parse the expression string and return an Expression object you can use for repeated evaluation.
|
||||
* <p>Some examples:
|
||||
* <pre>
|
||||
* 3 + 4
|
||||
* name.firstName
|
||||
* </pre>
|
||||
* @param expressionString the raw expression string to parse
|
||||
* @return an evaluator for the parsed expression
|
||||
* @throws ParseException an exception occurred during parsing
|
||||
*/
|
||||
Expression parseExpression(String expressionString) throws ParseException;
|
||||
|
||||
/**
|
||||
* Parse the expression string and return an Expression object you can use for repeated evaluation.
|
||||
* <p>Some examples:
|
||||
* <pre>
|
||||
* 3 + 4
|
||||
* name.firstName
|
||||
* </pre>
|
||||
* @param expressionString the raw expression string to parse
|
||||
* @param context a context for influencing this expression parsing routine (optional)
|
||||
* @return an evaluator for the parsed expression
|
||||
* @throws ParseException an exception occurred during parsing
|
||||
*/
|
||||
Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface MethodExecutor {
|
||||
|
||||
/**
|
||||
* Execute a command using the specified arguments, and using the specified expression state.
|
||||
* @param context the evaluation context in which the command is being executed
|
||||
* @param target the target object of the call - null for static methods
|
||||
* @param 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
|
||||
*/
|
||||
TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression;
|
||||
|
||||
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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0.1
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
*/
|
||||
List<Method> filter(List<Method> methods);
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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</code> 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
|
||||
*/
|
||||
MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,
|
||||
List<TypeDescriptor> argumentTypes) throws AccessException;
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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.
|
||||
* @param operation the operation to be performed
|
||||
* @param leftOperand the left operand
|
||||
* @param rightOperand the right operand
|
||||
* @return true if the OperatorOverloader supports the specified operation between the two operands
|
||||
* @throws EvaluationException if there is a problem performing the operation
|
||||
*/
|
||||
boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand)
|
||||
throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Execute the specified operation on two operands, returning a result.
|
||||
* See {@link Operation} for supported operations.
|
||||
* @param operation the operation to be performed
|
||||
* @param leftOperand the left operand
|
||||
* @param rightOperand the right operand
|
||||
* @return the result of performing the operation on the two operands
|
||||
* @throws EvaluationException if there is a problem performing the operation
|
||||
*/
|
||||
Object operate(Operation operation, Object leftOperand, Object rightOperand)
|
||||
throws EvaluationException;
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* Represent an exception that occurs during expression parsing.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ParseException extends ExpressionException {
|
||||
|
||||
/**
|
||||
* Creates a new expression parsing exception.
|
||||
* @param expressionString the expression string that could not be parsed
|
||||
* @param position the position in the expression string where the problem occurred
|
||||
* @param message description of the problem that occurred
|
||||
*/
|
||||
public ParseException(String expressionString, int position, String message) {
|
||||
super(expressionString, position, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression parsing exception.
|
||||
* @param position the position in the expression string where the problem occurred
|
||||
* @param message description of the problem that occurred
|
||||
* @param cause the underlying cause of this exception
|
||||
*/
|
||||
public ParseException(int position, String message, Throwable cause) {
|
||||
super(position, message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new expression parsing exception.
|
||||
* @param position the position in the expression string where the problem occurred
|
||||
* @param message description of the problem that occurred
|
||||
*/
|
||||
public ParseException(int position, String message) {
|
||||
super(position, message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* Input provided to an expression parser that can influence an expression parsing/compilation routine.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface ParserContext {
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* 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: "${"
|
||||
*
|
||||
* @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: "}"
|
||||
*
|
||||
* @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 }.
|
||||
* @see #isTemplate()
|
||||
*/
|
||||
public static final ParserContext TEMPLATE_EXPRESSION = new ParserContext() {
|
||||
|
||||
public String getExpressionPrefix() {
|
||||
return "#{";
|
||||
}
|
||||
|
||||
public String getExpressionSuffix() {
|
||||
return "}";
|
||||
}
|
||||
|
||||
public boolean isTemplate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
Class[] getSpecificTargetClasses();
|
||||
|
||||
/**
|
||||
* Called to determine if a resolver instance is able to access a specified property on a specified target object.
|
||||
* @param context the evaluation context in which the access is being attempted
|
||||
* @param target the target object upon which the property is being accessed
|
||||
* @param name the name of the property being accessed
|
||||
* @return true if this resolver is able to read the property
|
||||
* @throws AccessException if there is any problem determining whether the property can be read
|
||||
*/
|
||||
boolean canRead(EvaluationContext context, Object target, String name) throws AccessException;
|
||||
|
||||
/**
|
||||
* Called to read a property from a specified target object
|
||||
* @param context the evaluation context in which the access is being attempted
|
||||
* @param target the target object upon which the property is being accessed
|
||||
* @param name the name of the property being accessed
|
||||
* @return a TypedValue object wrapping the property value read and a type descriptor for it
|
||||
* @throws AccessException if there is any problem accessing the property value
|
||||
*/
|
||||
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.
|
||||
* @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
|
||||
*/
|
||||
boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException;
|
||||
|
||||
/**
|
||||
* Called to write to a property on a specified target object. Should only succeed if canWrite() also returns true.
|
||||
* @param context the evaluation context in which the access is being attempted
|
||||
* @param target the target object upon which the property is being accessed
|
||||
* @param name the name of the property being accessed
|
||||
* @param newValue the new value for the property
|
||||
* @throws AccessException if there is any problem writing to the property value
|
||||
*/
|
||||
void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException;
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
int compare(Object firstObject, Object secondObject) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Return true if the comparator can compare these two objects
|
||||
* @param firstObject the first object
|
||||
* @param secondObject the second object
|
||||
* @return true if the comparator can compare these objects
|
||||
*/
|
||||
boolean canCompare(Object firstObject, Object secondObject);
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface TypeConverter {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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<Integer> 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
|
||||
* @return the converted value
|
||||
* @throws EvaluationException if conversion is not possible
|
||||
*/
|
||||
Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType);
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
/**
|
||||
* Implementors of this interface are expected to be able to locate types. They may use custom classloaders
|
||||
* or the and deal with common package prefixes (java.lang, etc) however they wish. See
|
||||
* {@link org.springframework.expression.spel.support.StandardTypeLocator} for an example implementation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface TypeLocator {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
Class<?> findType(String typename) throws EvaluationException;
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression;
|
||||
|
||||
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()</code> call on the object.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class TypedValue {
|
||||
|
||||
public static final TypedValue NULL = new TypedValue(null);
|
||||
|
||||
|
||||
private final Object value;
|
||||
|
||||
private TypeDescriptor typeDescriptor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a TypedValue for a simple object. The type descriptor is inferred
|
||||
* from the object, so no generic information is preserved.
|
||||
* @param value the object value
|
||||
*/
|
||||
public TypedValue(Object value) {
|
||||
this.value = value;
|
||||
this.typeDescriptor = null; // initialized when/if requested
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TypedValue for a particular value with a particular type descriptor.
|
||||
* @param value the object value
|
||||
* @param typeDescriptor a type descriptor describing the type of the value
|
||||
*/
|
||||
public TypedValue(Object value, TypeDescriptor typeDescriptor) {
|
||||
this.value = value;
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
|
||||
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public TypeDescriptor getTypeDescriptor() {
|
||||
if (this.typeDescriptor == null) {
|
||||
this.typeDescriptor = TypeDescriptor.forObject(this.value);
|
||||
}
|
||||
return this.typeDescriptor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append("TypedValue: '").append(this.value).append("' of [").append(getTypeDescriptor() + "]");
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.common;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
|
||||
/**
|
||||
* Represents a template expression broken into pieces. Each piece will be an Expression but pure text parts to the
|
||||
* template will be represented as LiteralExpression objects. An example of a template expression might be:
|
||||
*
|
||||
* <pre class="code">
|
||||
* "Hello ${getName()}"</pre>
|
||||
*
|
||||
* which will be represented as a CompositeStringExpression of two parts. The first part being a
|
||||
* LiteralExpression representing 'Hello ' and the second part being a real expression that will
|
||||
* call <code>getName()</code> when invoked.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CompositeStringExpression implements Expression {
|
||||
|
||||
private final String expressionString;
|
||||
|
||||
/** The array of expressions that make up the composite expression */
|
||||
private final Expression[] expressions;
|
||||
|
||||
|
||||
public CompositeStringExpression(String expressionString, Expression[] expressions) {
|
||||
this.expressionString = expressionString;
|
||||
this.expressions = expressions;
|
||||
}
|
||||
|
||||
|
||||
public final String getExpressionString() {
|
||||
return this.expressionString;
|
||||
}
|
||||
|
||||
public String getValue() throws EvaluationException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Expression expression : this.expressions) {
|
||||
String value = expression.getValue(String.class);
|
||||
if (value != null) {
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getValue(Object rootObject) throws EvaluationException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Expression expression : this.expressions) {
|
||||
String value = expression.getValue(rootObject, String.class);
|
||||
if (value != null) {
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getValue(EvaluationContext context) throws EvaluationException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Expression expression : this.expressions) {
|
||||
String value = expression.getValue(context, String.class);
|
||||
if (value != null) {
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Expression expression : this.expressions) {
|
||||
String value = expression.getValue(context, rootObject, String.class);
|
||||
if (value != null) {
|
||||
sb.append(value);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public Class getValueType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor() {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
|
||||
throw new EvaluationException(this.expressionString, "Cannot call setValue on a composite expression");
|
||||
}
|
||||
|
||||
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
|
||||
Object value = getValue(context);
|
||||
return ExpressionUtils.convert(context, value, expectedResultType);
|
||||
}
|
||||
|
||||
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
|
||||
Object value = getValue();
|
||||
return ExpressionUtils.convert(null, value, expectedResultType);
|
||||
}
|
||||
|
||||
public boolean isWritable(EvaluationContext context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Expression[] getExpressions() {
|
||||
return expressions;
|
||||
}
|
||||
|
||||
|
||||
public <T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException {
|
||||
Object value = getValue(rootObject);
|
||||
return ExpressionUtils.convert(null, value, desiredResultType);
|
||||
}
|
||||
|
||||
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType)
|
||||
throws EvaluationException {
|
||||
Object value = getValue(context,rootObject);
|
||||
return ExpressionUtils.convert(context, value, desiredResultType);
|
||||
}
|
||||
|
||||
public Class getValueType(Object rootObject) throws EvaluationException {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
|
||||
throw new EvaluationException(this.expressionString, "Cannot call setValue on a composite expression");
|
||||
}
|
||||
|
||||
public boolean isWritable(Object rootObject) throws EvaluationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setValue(Object rootObject, Object value) throws EvaluationException {
|
||||
throw new EvaluationException(this.expressionString, "Cannot call setValue on a composite expression");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.common;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Common utility functions that may be used by any Expression Language provider.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class ExpressionUtils {
|
||||
|
||||
/**
|
||||
* Determines if there is a type converter available in the specified context and attempts to use it to convert the
|
||||
* supplied value to the specified type. Throws an exception if conversion is not possible.
|
||||
* @param context the evaluation context that may define a type converter
|
||||
* @param value the value to convert (may be null)
|
||||
* @param targetType the type to attempt conversion to
|
||||
* @return the converted value
|
||||
* @throws EvaluationException if there is a problem during conversion or conversion of the value to the specified
|
||||
* type is not supported
|
||||
*/
|
||||
public static <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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T convertTypedValue(EvaluationContext context, TypedValue typedValue, Class<T> targetType) {
|
||||
Object value = typedValue.getValue();
|
||||
if (targetType == null || ClassUtils.isAssignableValue(targetType, value)) {
|
||||
return (T) value;
|
||||
}
|
||||
if (context != null) {
|
||||
return (T) context.getTypeConverter().convertValue(value, typedValue.getTypeDescriptor(), TypeDescriptor.valueOf(targetType));
|
||||
}
|
||||
throw new EvaluationException("Cannot convert value '" + value + "' to type '" + targetType.getName() + "'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to an int using the supplied type converter.
|
||||
*/
|
||||
public static int toInt(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Integer) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
|
||||
TypeDescriptor.valueOf(Integer.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to a boolean using the supplied type converter.
|
||||
*/
|
||||
public static boolean toBoolean(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Boolean) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
|
||||
TypeDescriptor.valueOf(Boolean.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to a double using the supplied type converter.
|
||||
*/
|
||||
public static double toDouble(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Double) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
|
||||
TypeDescriptor.valueOf(Double.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to a long using the supplied type converter.
|
||||
*/
|
||||
public static long toLong(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Long) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(), TypeDescriptor
|
||||
.valueOf(Long.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to a char using the supplied type converter.
|
||||
*/
|
||||
public static char toChar(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Character) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(),
|
||||
TypeDescriptor.valueOf(Character.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to a short using the supplied type converter.
|
||||
*/
|
||||
public static short toShort(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Short) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(), TypeDescriptor
|
||||
.valueOf(Short.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to a float using the supplied type converter.
|
||||
*/
|
||||
public static float toFloat(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Float) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(), TypeDescriptor
|
||||
.valueOf(Float.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to convert a typed value to a byte using the supplied type converter.
|
||||
*/
|
||||
public static byte toByte(TypeConverter typeConverter, TypedValue typedValue) {
|
||||
return (Byte) typeConverter.convertValue(typedValue.getValue(), typedValue.getTypeDescriptor(), TypeDescriptor
|
||||
.valueOf(Byte.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.common;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
|
||||
/**
|
||||
* A very simple hardcoded implementation of the Expression interface that represents a string literal.
|
||||
* It is used with CompositeStringExpression when representing a template expression which is made up
|
||||
* of pieces - some being real expressions to be handled by an EL implementation like Spel, and some
|
||||
* being just textual elements.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class LiteralExpression implements Expression {
|
||||
|
||||
/** Fixed literal value of this expression */
|
||||
private final String literalValue;
|
||||
|
||||
|
||||
public LiteralExpression(String literalValue) {
|
||||
this.literalValue = literalValue;
|
||||
}
|
||||
|
||||
|
||||
public final String getExpressionString() {
|
||||
return this.literalValue;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return this.literalValue;
|
||||
}
|
||||
|
||||
public String getValue(EvaluationContext context) {
|
||||
return this.literalValue;
|
||||
}
|
||||
|
||||
public String getValue(Object rootObject) {
|
||||
return this.literalValue;
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context) {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor() {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
|
||||
throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
|
||||
}
|
||||
|
||||
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
|
||||
Object value = getValue(context);
|
||||
return ExpressionUtils.convert(context, value, expectedResultType);
|
||||
}
|
||||
|
||||
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
|
||||
Object value = getValue();
|
||||
return ExpressionUtils.convert(null, value, expectedResultType);
|
||||
}
|
||||
|
||||
public boolean isWritable(EvaluationContext context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Class getValueType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public <T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException {
|
||||
Object value = getValue(rootObject);
|
||||
return ExpressionUtils.convert(null, value, desiredResultType);
|
||||
}
|
||||
|
||||
public String getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
return this.literalValue;
|
||||
}
|
||||
|
||||
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType) throws EvaluationException {
|
||||
Object value = getValue(context, rootObject);
|
||||
return ExpressionUtils.convert(null, value, desiredResultType);
|
||||
}
|
||||
|
||||
public Class getValueType(Object rootObject) throws EvaluationException {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
return TypeDescriptor.valueOf(String.class);
|
||||
}
|
||||
|
||||
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
|
||||
throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
|
||||
}
|
||||
|
||||
public boolean isWritable(Object rootObject) throws EvaluationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setValue(Object rootObject, Object value) throws EvaluationException {
|
||||
throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.common;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
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.
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class TemplateAwareExpressionParser implements ExpressionParser {
|
||||
|
||||
/**
|
||||
* Default ParserContext instance for non-template expressions.
|
||||
*/
|
||||
private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
|
||||
public String getExpressionPrefix() {
|
||||
return null;
|
||||
}
|
||||
public String getExpressionSuffix() {
|
||||
return null;
|
||||
}
|
||||
public boolean isTemplate() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Expression parseExpression(String expressionString) throws ParseException {
|
||||
return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
|
||||
}
|
||||
|
||||
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 {
|
||||
return doParseExpression(expressionString, context);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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 '.
|
||||
*
|
||||
* @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 {
|
||||
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);
|
||||
if (prefixIndex >= startIdx) {
|
||||
// an inner expression was found - this is a composite
|
||||
if (prefixIndex > startIdx) {
|
||||
expressions.add(createLiteralExpression(context,expressionString.substring(startIdx, prefixIndex)));
|
||||
}
|
||||
int afterPrefixIndex = prefixIndex + prefix.length();
|
||||
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));
|
||||
}
|
||||
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();
|
||||
}
|
||||
} else {
|
||||
// no more ${expressions} found in string, add rest as static text
|
||||
expressions.add(createLiteralExpression(context,expressionString.substring(startIdx)));
|
||||
startIdx = expressionString.length();
|
||||
}
|
||||
}
|
||||
return expressions.toArray(new Expression[expressions.size()]);
|
||||
}
|
||||
|
||||
private Expression createLiteralExpression(ParserContext context, String text) {
|
||||
return new LiteralExpression(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return
|
||||
*/
|
||||
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++)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (suffixPosition!=suffix.length()) {
|
||||
// the expressionString ran out before the suffix could entirely be found
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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 {
|
||||
// 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 ) {
|
||||
return -1; // the suffix is missing
|
||||
}
|
||||
Stack<Bracket> stack = new Stack<Bracket>();
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (!isSuffixHere(expressionString, pos, suffix)) {
|
||||
return -1;
|
||||
}
|
||||
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 {}
|
||||
*/
|
||||
private static class 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==']';
|
||||
}
|
||||
return closeBracket==')';
|
||||
}
|
||||
|
||||
static char theOpenBracketFor(char closeBracket) {
|
||||
if (closeBracket=='}') {
|
||||
return '{';
|
||||
} else if (closeBracket==']') {
|
||||
return '[';
|
||||
}
|
||||
return '(';
|
||||
}
|
||||
|
||||
static char theCloseBracketFor(char openBracket) {
|
||||
if (openBracket=='{') {
|
||||
return '}';
|
||||
} else if (openBracket=='[') {
|
||||
return ']';
|
||||
}
|
||||
return ')';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually parse the expression string and return an Expression object.
|
||||
* @param expressionString the raw expression string to parse
|
||||
* @param context a context for influencing this expression parsing routine (optional)
|
||||
* @return an evaluator for the parsed expression
|
||||
* @throws ParseException an exception occurred during parsing
|
||||
*/
|
||||
protected abstract Expression doParseExpression(String expressionString, ParserContext context)
|
||||
throws ParseException;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.common;
|
||||
|
||||
import org.springframework.expression.ParserContext;
|
||||
|
||||
/**
|
||||
* Configurable {@link ParserContext} implementation for template parsing.
|
||||
* Expects the expression prefix and suffix as constructor arguments.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class TemplateParserContext implements ParserContext {
|
||||
|
||||
private final String expressionPrefix;
|
||||
|
||||
private final String expressionSuffix;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new TemplateParserContext with the default "#{" prefix and "}" suffix.
|
||||
*/
|
||||
public TemplateParserContext() {
|
||||
this("#{", "}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new TemplateParserContext for the given prefix and suffix.
|
||||
* @param expressionPrefix the expression prefix to use
|
||||
* @param expressionSuffix the expression suffix to use
|
||||
*/
|
||||
public TemplateParserContext(String expressionPrefix, String expressionSuffix) {
|
||||
this.expressionPrefix = expressionPrefix;
|
||||
this.expressionSuffix = expressionSuffix;
|
||||
}
|
||||
|
||||
|
||||
public final boolean isTemplate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final String getExpressionPrefix() {
|
||||
return this.expressionPrefix;
|
||||
}
|
||||
|
||||
public final String getExpressionSuffix() {
|
||||
return this.expressionSuffix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.OperatorOverloader;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypeComparator;
|
||||
import org.springframework.expression.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.
|
||||
*
|
||||
* <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
|
||||
*/
|
||||
public class ExpressionState {
|
||||
|
||||
private final EvaluationContext relatedContext;
|
||||
|
||||
private Stack<VariableScope> variableScopes;
|
||||
|
||||
private Stack<TypedValue> contextObjects;
|
||||
|
||||
private final TypedValue rootObject;
|
||||
|
||||
private SpelParserConfiguration configuration;
|
||||
|
||||
|
||||
public ExpressionState(EvaluationContext context) {
|
||||
this.relatedContext = context;
|
||||
this.rootObject = context.getRootObject();
|
||||
}
|
||||
|
||||
public ExpressionState(EvaluationContext context, SpelParserConfiguration configuration) {
|
||||
this.relatedContext = context;
|
||||
this.configuration = configuration;
|
||||
this.rootObject = context.getRootObject();
|
||||
}
|
||||
|
||||
public ExpressionState(EvaluationContext context, TypedValue rootObject) {
|
||||
this.relatedContext = context;
|
||||
this.rootObject = rootObject;
|
||||
}
|
||||
|
||||
public ExpressionState(EvaluationContext context, TypedValue rootObject, SpelParserConfiguration configuration) {
|
||||
this.relatedContext = context;
|
||||
this.configuration = configuration;
|
||||
this.rootObject = rootObject;
|
||||
}
|
||||
|
||||
|
||||
private void ensureVariableScopesInitialized() {
|
||||
if (this.variableScopes == null) {
|
||||
this.variableScopes = new Stack<VariableScope>();
|
||||
// top level empty variable scope
|
||||
this.variableScopes.add(new VariableScope());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The active context object is what unqualified references to properties/etc are resolved against.
|
||||
*/
|
||||
public TypedValue getActiveContextObject() {
|
||||
if (this.contextObjects==null || this.contextObjects.isEmpty()) {
|
||||
return this.rootObject;
|
||||
}
|
||||
|
||||
return this.contextObjects.peek();
|
||||
}
|
||||
|
||||
public void pushActiveContextObject(TypedValue obj) {
|
||||
if (this.contextObjects==null) {
|
||||
this.contextObjects = new Stack<TypedValue>();
|
||||
}
|
||||
this.contextObjects.push(obj);
|
||||
}
|
||||
|
||||
public void popActiveContextObject() {
|
||||
if (this.contextObjects==null) {
|
||||
this.contextObjects = new Stack<TypedValue>();
|
||||
}
|
||||
this.contextObjects.pop();
|
||||
}
|
||||
|
||||
public TypedValue getRootContextObject() {
|
||||
return this.rootObject;
|
||||
}
|
||||
|
||||
public void setVariable(String name, Object value) {
|
||||
this.relatedContext.setVariable(name, value);
|
||||
}
|
||||
|
||||
public TypedValue lookupVariable(String name) {
|
||||
Object value = this.relatedContext.lookupVariable(name);
|
||||
if (value == null) {
|
||||
return TypedValue.NULL;
|
||||
}
|
||||
else {
|
||||
return new TypedValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
public TypeComparator getTypeComparator() {
|
||||
return this.relatedContext.getTypeComparator();
|
||||
}
|
||||
|
||||
public Class<?> findType(String type) throws EvaluationException {
|
||||
return this.relatedContext.getTypeLocator().findType(type);
|
||||
}
|
||||
|
||||
public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
|
||||
return this.relatedContext.getTypeConverter().convertValue(value, TypeDescriptor.forObject(value), targetTypeDescriptor);
|
||||
}
|
||||
|
||||
public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
|
||||
Object val = value.getValue();
|
||||
return this.relatedContext.getTypeConverter().convertValue(val, TypeDescriptor.forObject(val), targetTypeDescriptor);
|
||||
}
|
||||
|
||||
/*
|
||||
* A new scope is entered when a function is invoked
|
||||
*/
|
||||
|
||||
public void enterScope(Map<String, Object> argMap) {
|
||||
ensureVariableScopesInitialized();
|
||||
this.variableScopes.push(new VariableScope(argMap));
|
||||
}
|
||||
|
||||
public void enterScope(String name, Object value) {
|
||||
ensureVariableScopesInitialized();
|
||||
this.variableScopes.push(new VariableScope(name, value));
|
||||
}
|
||||
|
||||
public void exitScope() {
|
||||
ensureVariableScopesInitialized();
|
||||
this.variableScopes.pop();
|
||||
}
|
||||
|
||||
public void setLocalVariable(String name, Object value) {
|
||||
ensureVariableScopesInitialized();
|
||||
this.variableScopes.peek().setVariable(name, value);
|
||||
}
|
||||
|
||||
public Object lookupLocalVariable(String name) {
|
||||
ensureVariableScopesInitialized();
|
||||
int scopeNumber = this.variableScopes.size() - 1;
|
||||
for (int i = scopeNumber; i >= 0; i--) {
|
||||
if (this.variableScopes.get(i).definesVariable(name)) {
|
||||
return this.variableScopes.get(i).lookupVariable(name);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public TypedValue operate(Operation op, Object left, Object right) throws EvaluationException {
|
||||
OperatorOverloader overloader = this.relatedContext.getOperatorOverloader();
|
||||
if (overloader.overridesOperation(op, left, right)) {
|
||||
Object returnValue = overloader.operate(op, left, right);
|
||||
return new TypedValue(returnValue);
|
||||
}
|
||||
else {
|
||||
String leftType = (left==null?"null":left.getClass().getName());
|
||||
String rightType = (right==null?"null":right.getClass().getName());
|
||||
throw new SpelEvaluationException(SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, leftType, rightType);
|
||||
}
|
||||
}
|
||||
|
||||
public List<PropertyAccessor> getPropertyAccessors() {
|
||||
return this.relatedContext.getPropertyAccessors();
|
||||
}
|
||||
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
return this.relatedContext;
|
||||
}
|
||||
|
||||
public SpelParserConfiguration getConfiguration() {
|
||||
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
|
||||
* the function is executing. When the function returns the scope is exited.
|
||||
*/
|
||||
private static class VariableScope {
|
||||
|
||||
private final Map<String, Object> vars = new HashMap<String, Object>();
|
||||
|
||||
public VariableScope() { }
|
||||
|
||||
public VariableScope(Map<String, Object> arguments) {
|
||||
if (arguments != null) {
|
||||
this.vars.putAll(arguments);
|
||||
}
|
||||
}
|
||||
|
||||
public VariableScope(String name, Object value) {
|
||||
this.vars.put(name,value);
|
||||
}
|
||||
|
||||
public Object lookupVariable(String name) {
|
||||
return this.vars.get(name);
|
||||
}
|
||||
|
||||
public void setVariable(String name, Object value) {
|
||||
this.vars.put(name,value);
|
||||
}
|
||||
|
||||
public boolean definesVariable(String name) {
|
||||
return this.vars.containsKey(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.springframework.expression.spel.SpelParseException;
|
||||
|
||||
/**
|
||||
* Wraps a real parse exception. This exception flows to the top parse method and then
|
||||
* the wrapped exception is thrown as the real problem.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class InternalParseException extends RuntimeException {
|
||||
|
||||
public InternalParseException(SpelParseException cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public SpelParseException getCause() {
|
||||
return (SpelParseException) super.getCause();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
|
||||
/**
|
||||
* Root exception for Spring EL related exceptions. Rather than holding a hard coded string indicating the problem, it
|
||||
* records a message key and the inserts for the message. See {@link SpelMessage} for the list of all possible messages
|
||||
* that can occur.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpelEvaluationException extends EvaluationException {
|
||||
|
||||
private SpelMessage message;
|
||||
private Object[] inserts;
|
||||
|
||||
public SpelEvaluationException(SpelMessage message, Object... inserts) {
|
||||
super(message.formatMessage(0, inserts)); // TODO poor position information, can the callers not really supply something?
|
||||
this.message = message;
|
||||
this.inserts = inserts;
|
||||
}
|
||||
|
||||
public SpelEvaluationException(int position, SpelMessage message, Object... inserts) {
|
||||
super(position, message.formatMessage(position, inserts));
|
||||
this.message = message;
|
||||
this.inserts = inserts;
|
||||
}
|
||||
|
||||
public SpelEvaluationException(int position, Throwable cause,
|
||||
SpelMessage message, Object... inserts) {
|
||||
super(position,message.formatMessage(position,inserts),cause);
|
||||
this.message = message;
|
||||
this.inserts = inserts;
|
||||
}
|
||||
|
||||
public SpelEvaluationException(Throwable cause, SpelMessage message, Object... inserts) {
|
||||
super(message.formatMessage(0,inserts),cause);
|
||||
this.message = message;
|
||||
this.inserts = inserts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a formatted message with inserts applied
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (message != null)
|
||||
return message.formatMessage(position, inserts);
|
||||
else
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message code
|
||||
*/
|
||||
public SpelMessage getMessageCode() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the position in the related expression which gave rise to this exception.
|
||||
*
|
||||
* @param position the position in the expression that gave rise to the exception
|
||||
*/
|
||||
public void setPosition(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message inserts
|
||||
*/
|
||||
public Object[] getInserts() {
|
||||
return inserts;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
/**
|
||||
* Contains all the messages that can be produced by the Spring Expression Language. Each message has a kind (info,
|
||||
* warn, error) and a code number. Tests can be written to expect particular code numbers rather than particular text,
|
||||
* enabling the message text to more easily be modified and the tests to run successfully in different locales.
|
||||
* <p>
|
||||
* When a message is formatted, it will have this kind of form
|
||||
*
|
||||
* <pre>
|
||||
* 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.
|
||||
*
|
||||
* @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_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(Kind.ERROR, 1060,
|
||||
"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"), //
|
||||
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"), //
|
||||
;
|
||||
|
||||
private Kind kind;
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
|
||||
private SpelMessage(Kind kind, int code, String message) {
|
||||
this.kind = kind;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produce a complete message including the prefix, the position (if known) and with the inserts applied to the
|
||||
* message.
|
||||
*
|
||||
* @param pos the position, if less than zero it is ignored and not included in the message
|
||||
* @param inserts the inserts to put into the formatted message
|
||||
* @return a formatted message
|
||||
*/
|
||||
public String formatMessage(int pos, Object... inserts) {
|
||||
StringBuilder formattedMessage = new StringBuilder();
|
||||
formattedMessage.append("EL").append(code);
|
||||
switch (kind) {
|
||||
// case WARNING:
|
||||
// formattedMessage.append("W");
|
||||
// break;
|
||||
// case INFO:
|
||||
// formattedMessage.append("I");
|
||||
// break;
|
||||
case ERROR:
|
||||
formattedMessage.append("E");
|
||||
break;
|
||||
}
|
||||
formattedMessage.append(":");
|
||||
if (pos != -1) {
|
||||
formattedMessage.append("(pos ").append(pos).append("): ");
|
||||
}
|
||||
formattedMessage.append(MessageFormat.format(message, inserts));
|
||||
return formattedMessage.toString();
|
||||
}
|
||||
|
||||
|
||||
public static enum Kind {
|
||||
INFO, WARNING, ERROR
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
/**
|
||||
* Represents a node in the Ast for a parsed expression.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface SpelNode {
|
||||
|
||||
/**
|
||||
* Evaluate the expression node in the context of the supplied expression state and return the value.
|
||||
* @param expressionState the current expression state (includes the context)
|
||||
* @return the value of this node evaluated against the specified state
|
||||
*/
|
||||
Object getValue(ExpressionState expressionState) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* Determine if this expression node will support a setValue() call.
|
||||
*
|
||||
* @param expressionState the current expression state (includes the context)
|
||||
* @return true if the expression node will allow setValue()
|
||||
* @throws EvaluationException if something went wrong trying to determine if the node supports writing
|
||||
*/
|
||||
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.
|
||||
* @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
|
||||
*/
|
||||
void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException;
|
||||
|
||||
/**
|
||||
* @return the string form of this AST node
|
||||
*/
|
||||
String toStringAST();
|
||||
|
||||
/**
|
||||
* @return the number of children under this node
|
||||
*/
|
||||
int getChildCount();
|
||||
|
||||
/**
|
||||
* Helper method that returns a SpelNode rather than an Antlr Tree node.
|
||||
* @return the child node cast to a SpelNode
|
||||
*/
|
||||
SpelNode getChild(int index);
|
||||
|
||||
/**
|
||||
* Determine the class of the object passed in, unless it is already a class object.
|
||||
* @param o the object that the caller wants the class of
|
||||
* @return the class of the object if it is not already a class object, or null if the object is null
|
||||
*/
|
||||
Class<?> getObjectClass(Object obj);
|
||||
|
||||
/**
|
||||
* @return the start position of this Ast node in the expression string
|
||||
*/
|
||||
int getStartPosition();
|
||||
|
||||
/**
|
||||
* @return the end position of this Ast node in the expression string
|
||||
*/
|
||||
int getEndPosition();
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import org.springframework.expression.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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpelParseException extends ParseException {
|
||||
|
||||
private SpelMessage message;
|
||||
private 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));
|
||||
this.position = position;
|
||||
this.message = message;
|
||||
this.inserts = inserts;
|
||||
}
|
||||
|
||||
public SpelParseException(int position, SpelMessage message, Object... inserts) {
|
||||
super(position, message.formatMessage(position,inserts));
|
||||
this.position = position;
|
||||
this.message = message;
|
||||
this.inserts = inserts;
|
||||
}
|
||||
|
||||
public SpelParseException(int position, Throwable cause, SpelMessage message, Object... inserts) {
|
||||
super(position, message.formatMessage(position,inserts), cause);
|
||||
this.position = position;
|
||||
this.message = message;
|
||||
this.inserts = inserts;
|
||||
}
|
||||
|
||||
//
|
||||
// public SpelException(Throwable cause, SpelMessages message, Object... inserts) {
|
||||
// super(cause);
|
||||
// this.message = message;
|
||||
// this.inserts = inserts;
|
||||
// }
|
||||
//
|
||||
// public SpelException(int position, SpelMessages message, Object... inserts) {
|
||||
// super((Throwable)null);
|
||||
// this.position = position;
|
||||
// this.message = message;
|
||||
// this.inserts = inserts;
|
||||
// }
|
||||
//
|
||||
// public SpelException(SpelMessages message, Object... inserts) {
|
||||
// super((Throwable)null);
|
||||
// this.message = message;
|
||||
// this.inserts = inserts;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* @return a formatted message with inserts applied
|
||||
*/
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (message != null)
|
||||
return message.formatMessage(position, inserts);
|
||||
else
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message code
|
||||
*/
|
||||
public SpelMessage getMessageCode() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the message inserts
|
||||
*/
|
||||
public Object[] getInserts() {
|
||||
return inserts;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
/**
|
||||
* Configuration object for the SpEL expression parser.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see org.springframework.expression.spel.standard.SpelExpressionParser#SpelExpressionParser(SpelParserConfiguration)
|
||||
*/
|
||||
public class SpelParserConfiguration {
|
||||
|
||||
private final boolean autoGrowNullReferences;
|
||||
|
||||
private final boolean autoGrowCollections;
|
||||
|
||||
|
||||
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections) {
|
||||
this.autoGrowNullReferences = autoGrowNullReferences;
|
||||
this.autoGrowCollections = autoGrowCollections;
|
||||
}
|
||||
|
||||
|
||||
public boolean isAutoGrowNullReferences() {
|
||||
return this.autoGrowNullReferences;
|
||||
}
|
||||
|
||||
public boolean isAutoGrowCollections() {
|
||||
return this.autoGrowCollections;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
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.
|
||||
*
|
||||
* <p>Example: 'someNumberProperty=42'
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* Utilities methods for use in the Ast classes.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0.2
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, ExpressionState state) {
|
||||
List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
|
||||
List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
|
||||
for (PropertyAccessor resolver : state.getPropertyAccessors()) {
|
||||
Class<?>[] targets = resolver.getSpecificTargetClasses();
|
||||
if (targets == null) { // generic resolver that says it can be used for any type
|
||||
generalAccessors.add(resolver);
|
||||
}
|
||||
else {
|
||||
if (targetType != null) {
|
||||
int pos = 0;
|
||||
for (Class<?> clazz : targets) {
|
||||
if (clazz == targetType) { // put exact matches on the front to be tried first?
|
||||
specificAccessors.add(pos++, resolver);
|
||||
}
|
||||
else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the
|
||||
// specificAccessor list
|
||||
generalAccessors.add(resolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
List<PropertyAccessor> resolvers = new ArrayList<PropertyAccessor>();
|
||||
resolvers.addAll(specificAccessors);
|
||||
resolvers.addAll(generalAccessors);
|
||||
return resolvers;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
|
||||
/**
|
||||
* Represents a bean reference to a type, for example "@foo" or "@'foo.bar'"
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class BeanReference extends SpelNodeImpl {
|
||||
|
||||
private 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);
|
||||
}
|
||||
try {
|
||||
TypedValue bean = new TypedValue(beanResolver.resolve(state.getEvaluationContext(),beanname));
|
||||
return bean;
|
||||
} catch (AccessException ae) {
|
||||
throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,
|
||||
beanname, ae.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("@");
|
||||
if (beanname.indexOf('.')==-1) {
|
||||
sb.append(beanname);
|
||||
} else {
|
||||
sb.append("'").append(beanname).append("'");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Represents the literal values TRUE and FALSE.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
|
||||
/**
|
||||
* Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CompoundExpression extends SpelNodeImpl {
|
||||
|
||||
public CompoundExpression(int pos,SpelNodeImpl... expressionComponents) {
|
||||
super(pos,expressionComponents);
|
||||
if (expressionComponents.length<2) {
|
||||
throw new IllegalStateException("Dont build compound expression less than one entry: "+expressionComponents.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evalutes a compound expression. This involves evaluating each piece in turn and the return value from each piece
|
||||
* is the active context object for the subsequent piece.
|
||||
* @param state the state in which the expression is being evaluated
|
||||
* @return the final value from the last piece of the compound expression
|
||||
*/
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue result = null;
|
||||
SpelNodeImpl nextNode = null;
|
||||
try {
|
||||
nextNode = children[0];
|
||||
result = nextNode.getValueInternal(state);
|
||||
for (int i = 1; i < getChildCount(); i++) {
|
||||
try {
|
||||
state.pushActiveContextObject(result);
|
||||
nextNode = children[i];
|
||||
result = nextNode.getValueInternal(state);
|
||||
} finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
} catch (SpelEvaluationException ee) {
|
||||
// Correct the position for the error before rethrowing
|
||||
ee.setPosition(nextNode.getStartPosition());
|
||||
throw ee;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ExpressionState state, Object value) throws EvaluationException {
|
||||
if (getChildCount() == 1) {
|
||||
getChild(0).setValue(state, value);
|
||||
return;
|
||||
}
|
||||
TypedValue ctx = children[0].getValueInternal(state);
|
||||
for (int i = 1; i < getChildCount() - 1; i++) {
|
||||
try {
|
||||
state.pushActiveContextObject(ctx);
|
||||
ctx = children[i].getValueInternal(state);
|
||||
} finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
try {
|
||||
state.pushActiveContextObject(ctx);
|
||||
getChild(getChildCount() - 1).setValue(state, value);
|
||||
} finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable(ExpressionState state) throws EvaluationException {
|
||||
if (getChildCount() == 1) {
|
||||
return getChild(0).isWritable(state);
|
||||
}
|
||||
TypedValue ctx = children[0].getValueInternal(state);
|
||||
for (int i = 1; i < getChildCount() - 1; i++) {
|
||||
try {
|
||||
state.pushActiveContextObject(ctx);
|
||||
ctx = children[i].getValueInternal(state);
|
||||
} finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
try {
|
||||
state.pushActiveContextObject(ctx);
|
||||
return getChild(getChildCount() - 1).isWritable(state);
|
||||
} finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
if (i>0) { sb.append("."); }
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,404 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.ConstructorExecutor;
|
||||
import org.springframework.expression.ConstructorResolver;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.common.ExpressionUtils;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
|
||||
/**
|
||||
* Represents the invocation of a constructor. Either a constructor on a regular type or
|
||||
* construction of an array. When an array is constructed, an initializer can be specified.
|
||||
*
|
||||
* <p>
|
||||
* Examples:<br>
|
||||
* new String('hello world')<br>
|
||||
* new int[]{1,2,3,4}<br>
|
||||
* new int[3] new int[3]{1,2,3}
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConstructorReference extends SpelNodeImpl {
|
||||
|
||||
private boolean isArrayConstructor = false;
|
||||
|
||||
private SpelNodeImpl[] dimensions;
|
||||
|
||||
// TODO is this caching safe - passing the expression around will mean this executor is also being passed around
|
||||
/**
|
||||
* The cached executor that may be reused on subsequent evaluations.
|
||||
*/
|
||||
private volatile ConstructorExecutor cachedExecutor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a constructor reference. The first argument is the type, the rest are the parameters to the constructor
|
||||
* call
|
||||
*/
|
||||
public ConstructorReference(int pos, SpelNodeImpl... arguments) {
|
||||
super(pos, arguments);
|
||||
this.isArrayConstructor = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a constructor reference. The first argument is the type, the rest are the parameters to the constructor
|
||||
* call
|
||||
*/
|
||||
public ConstructorReference(int pos, SpelNodeImpl[] dimensions, SpelNodeImpl... arguments) {
|
||||
super(pos, arguments);
|
||||
this.isArrayConstructor = true;
|
||||
this.dimensions = dimensions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Implements getValue() - delegating to the code for building an array or a simple type.
|
||||
*/
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
if (this.isArrayConstructor) {
|
||||
return createArray(state);
|
||||
}
|
||||
else {
|
||||
return createNewInstance(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ordinary object and return it.
|
||||
* @param state the expression state within which this expression is being evaluated
|
||||
* @return the new object
|
||||
* @throws EvaluationException if there is a problem creating the object
|
||||
*/
|
||||
private TypedValue createNewInstance(ExpressionState state) throws EvaluationException {
|
||||
Object[] arguments = new Object[getChildCount() - 1];
|
||||
List<TypeDescriptor> argumentTypes = new ArrayList<TypeDescriptor>(getChildCount() - 1);
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
TypedValue childValue = this.children[i + 1].getValueInternal(state);
|
||||
Object value = childValue.getValue();
|
||||
arguments[i] = value;
|
||||
argumentTypes.add(TypeDescriptor.forObject(value));
|
||||
}
|
||||
|
||||
ConstructorExecutor executorToUse = this.cachedExecutor;
|
||||
if (executorToUse != null) {
|
||||
try {
|
||||
return executorToUse.execute(state.getEvaluationContext(), arguments);
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
// Two reasons this can occur:
|
||||
// 1. the method invoked actually threw a real exception
|
||||
// 2. the method invoked was not passed the arguments it expected and has become 'stale'
|
||||
|
||||
// In the first case we should not retry, in the second case we should see if there is a
|
||||
// better suited method.
|
||||
|
||||
// To determine which situation it is, the AccessException will contain a cause.
|
||||
// If the cause is an InvocationTargetException, a user exception was thrown inside the constructor.
|
||||
// Otherwise the constructor could not be invoked.
|
||||
if (ae.getCause() instanceof InvocationTargetException) {
|
||||
// User exception was the root cause - exit now
|
||||
Throwable rootCause = ae.getCause().getCause();
|
||||
if (rootCause instanceof RuntimeException) {
|
||||
throw (RuntimeException) rootCause;
|
||||
} else {
|
||||
String typename = (String) this.children[0].getValueInternal(state).getValue();
|
||||
throw new SpelEvaluationException(getStartPosition(), rootCause,
|
||||
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename, FormatHelper
|
||||
.formatMethodForMessage("", argumentTypes));
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we know it wasn't a user problem so worth a retry if a better candidate can be found
|
||||
this.cachedExecutor = null;
|
||||
}
|
||||
}
|
||||
|
||||
// either there was no accessor or it no longer exists
|
||||
String typename = (String) this.children[0].getValueInternal(state).getValue();
|
||||
executorToUse = findExecutorForConstructor(typename, argumentTypes, state);
|
||||
try {
|
||||
this.cachedExecutor = executorToUse;
|
||||
return executorToUse.execute(state.getEvaluationContext(), arguments);
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM,
|
||||
typename, FormatHelper.formatMethodForMessage("", argumentTypes));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go through the list of registered constructor resolvers and see if any can find a constructor that takes the
|
||||
* specified set of arguments.
|
||||
* @param typename the type trying to be constructed
|
||||
* @param argumentTypes the types of the arguments supplied that the constructor must take
|
||||
* @param state the current state of the expression
|
||||
* @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
|
||||
* @throws SpelEvaluationException if there is a problem locating the constructor
|
||||
*/
|
||||
private ConstructorExecutor findExecutorForConstructor(String typename, List<TypeDescriptor> argumentTypes,
|
||||
ExpressionState state) throws SpelEvaluationException {
|
||||
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
List<ConstructorResolver> cResolvers = eContext.getConstructorResolvers();
|
||||
if (cResolvers != null) {
|
||||
for (ConstructorResolver ctorResolver : cResolvers) {
|
||||
try {
|
||||
ConstructorExecutor cEx = ctorResolver.resolve(state.getEvaluationContext(), typename,
|
||||
argumentTypes);
|
||||
if (cEx != null) {
|
||||
return cEx;
|
||||
}
|
||||
}
|
||||
catch (AccessException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename,
|
||||
FormatHelper.formatMethodForMessage("", argumentTypes));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.CONSTRUCTOR_NOT_FOUND, typename, FormatHelper
|
||||
.formatMethodForMessage("", argumentTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("new ");
|
||||
|
||||
int index = 0;
|
||||
sb.append(getChild(index++).toStringAST());
|
||||
sb.append("(");
|
||||
for (int i = index; i < getChildCount(); i++) {
|
||||
if (i > index)
|
||||
sb.append(",");
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array and return it.
|
||||
* @param state the expression state within which this expression is being evaluated
|
||||
* @return the new array
|
||||
* @throws EvaluationException if there is a problem creating the array
|
||||
*/
|
||||
private TypedValue createArray(ExpressionState state) throws EvaluationException {
|
||||
// First child gives us the array type which will either be a primitive or reference type
|
||||
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()));
|
||||
}
|
||||
String type = (String) intendedArrayType;
|
||||
Class<?> componentType;
|
||||
TypeCode arrayTypeCode = TypeCode.forName(type);
|
||||
if (arrayTypeCode == TypeCode.OBJECT) {
|
||||
componentType = state.findType(type);
|
||||
}
|
||||
else {
|
||||
componentType = arrayTypeCode.getType();
|
||||
}
|
||||
Object newArray;
|
||||
if (!hasInitializer()) {
|
||||
// Confirm all dimensions were specified (for example [3][][5] is missing the 2nd dimension)
|
||||
for (SpelNodeImpl dimension : this.dimensions) {
|
||||
if (dimension == null) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.MISSING_ARRAY_DIMENSION);
|
||||
}
|
||||
}
|
||||
TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
|
||||
|
||||
// Shortcut for 1 dimensional
|
||||
if (this.dimensions.length == 1) {
|
||||
TypedValue o = this.dimensions[0].getTypedValue(state);
|
||||
int arraySize = ExpressionUtils.toInt(typeConverter, o);
|
||||
newArray = Array.newInstance(componentType, arraySize);
|
||||
}
|
||||
else {
|
||||
// Multi-dimensional - hold onto your hat!
|
||||
int[] dims = new int[this.dimensions.length];
|
||||
for (int d = 0; d < this.dimensions.length; d++) {
|
||||
TypedValue o = this.dimensions[d].getTypedValue(state);
|
||||
dims[d] = ExpressionUtils.toInt(typeConverter, o);
|
||||
}
|
||||
newArray = Array.newInstance(componentType, dims);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// There is an initializer
|
||||
if (this.dimensions.length > 1) {
|
||||
// There is an initializer but this is a multi-dimensional array (e.g. new int[][]{{1,2},{3,4}}) - this
|
||||
// is not currently supported
|
||||
throw new SpelEvaluationException(getStartPosition(),
|
||||
SpelMessage.MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED);
|
||||
}
|
||||
TypeConverter typeConverter = state.getEvaluationContext().getTypeConverter();
|
||||
InlineList initializer = (InlineList) getChild(1);
|
||||
// If a dimension was specified, check it matches the initializer length
|
||||
if (this.dimensions[0] != null) {
|
||||
TypedValue dValue = this.dimensions[0].getTypedValue(state);
|
||||
int i = ExpressionUtils.toInt(typeConverter, dValue);
|
||||
if (i != initializer.getChildCount()) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INITIALIZER_LENGTH_INCORRECT);
|
||||
}
|
||||
}
|
||||
// Build the array and populate it
|
||||
int arraySize = initializer.getChildCount();
|
||||
newArray = Array.newInstance(componentType, arraySize);
|
||||
if (arrayTypeCode == TypeCode.OBJECT) {
|
||||
populateReferenceTypeArray(state, newArray, typeConverter, initializer, componentType);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.INT) {
|
||||
populateIntArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.BOOLEAN) {
|
||||
populateBooleanArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.CHAR) {
|
||||
populateCharArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.LONG) {
|
||||
populateLongArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.SHORT) {
|
||||
populateShortArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.DOUBLE) {
|
||||
populateDoubleArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.FLOAT) {
|
||||
populateFloatArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else if (arrayTypeCode == TypeCode.BYTE) {
|
||||
populateByteArray(state, newArray, typeConverter, initializer);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(arrayTypeCode.name());
|
||||
}
|
||||
}
|
||||
return new TypedValue(newArray);
|
||||
}
|
||||
|
||||
private void populateReferenceTypeArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer, Class<?> componentType) {
|
||||
TypeDescriptor toTypeDescriptor = TypeDescriptor.valueOf(componentType);
|
||||
Object[] newObjectArray = (Object[]) newArray;
|
||||
for (int i = 0; i < newObjectArray.length; i++) {
|
||||
SpelNode elementNode = initializer.getChild(i);
|
||||
Object arrayEntry = elementNode.getValue(state);
|
||||
newObjectArray[i] = typeConverter.convertValue(arrayEntry, TypeDescriptor.forObject(arrayEntry), toTypeDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateByteArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer) {
|
||||
byte[] newByteArray = (byte[]) newArray;
|
||||
for (int i = 0; i < newByteArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newByteArray[i] = ExpressionUtils.toByte(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateFloatArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer) {
|
||||
float[] newFloatArray = (float[]) newArray;
|
||||
for (int i = 0; i < newFloatArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newFloatArray[i] = ExpressionUtils.toFloat(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateDoubleArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer) {
|
||||
double[] newDoubleArray = (double[]) newArray;
|
||||
for (int i = 0; i < newDoubleArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newDoubleArray[i] = ExpressionUtils.toDouble(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateShortArray(ExpressionState state, Object newArray,
|
||||
TypeConverter typeConverter, InlineList initializer) {
|
||||
short[] newShortArray = (short[]) newArray;
|
||||
for (int i = 0; i < newShortArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newShortArray[i] = ExpressionUtils.toShort(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateLongArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer) {
|
||||
long[] newLongArray = (long[]) newArray;
|
||||
for (int i = 0; i < newLongArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newLongArray[i] = ExpressionUtils.toLong(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateCharArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer) {
|
||||
char[] newCharArray = (char[]) newArray;
|
||||
for (int i = 0; i < newCharArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newCharArray[i] = ExpressionUtils.toChar(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateBooleanArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer) {
|
||||
boolean[] newBooleanArray = (boolean[]) newArray;
|
||||
for (int i = 0; i < newBooleanArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newBooleanArray[i] = ExpressionUtils.toBoolean(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateIntArray(ExpressionState state, Object newArray, TypeConverter typeConverter,
|
||||
InlineList initializer) {
|
||||
int[] newIntArray = (int[]) newArray;
|
||||
for (int i = 0; i < newIntArray.length; i++) {
|
||||
TypedValue typedValue = initializer.getChild(i).getTypedValue(state);
|
||||
newIntArray[i] = ExpressionUtils.toInt(typeConverter, typedValue);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasInitializer() {
|
||||
return getChildCount() > 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
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".
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Elvis extends SpelNodeImpl {
|
||||
|
||||
public Elvis(int pos, SpelNodeImpl... args) {
|
||||
super(pos,args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@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)) {
|
||||
return value;
|
||||
} else {
|
||||
return children[1].getValueInternal(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
return new StringBuilder().append(getChild(0).toStringAST()).append(" ?: ").append(getChild(1).toStringAST()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
|
||||
/**
|
||||
* Utility methods (formatters, etc) used during parsing and evaluation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class FormatHelper {
|
||||
|
||||
/**
|
||||
* Produce a nice string for a given method name with specified arguments.
|
||||
* @param name the name of the method
|
||||
* @param argumentTypes the types of the arguments to the method
|
||||
* @return nicely formatted string, eg. foo(String,int)
|
||||
*/
|
||||
public static String formatMethodForMessage(String name, List<TypeDescriptor> argumentTypes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(name);
|
||||
sb.append("(");
|
||||
for (int i = 0; i < argumentTypes.size(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
TypeDescriptor typeDescriptor = argumentTypes.get(i);
|
||||
if (typeDescriptor != null) {
|
||||
sb.append(formatClassNameForMessage(typeDescriptor.getType()));
|
||||
}
|
||||
else {
|
||||
sb.append(formatClassNameForMessage(null));
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a nice string for a given class object.
|
||||
* For example, a string array will have the formatted name "java.lang.String[]".
|
||||
* @param clazz The class whose name is to be formatted
|
||||
* @return a formatted string suitable for message inclusion
|
||||
*/
|
||||
public static String formatClassNameForMessage(Class<?> clazz) {
|
||||
if (clazz == null) {
|
||||
return "null";
|
||||
}
|
||||
StringBuilder fmtd = new StringBuilder();
|
||||
if (clazz.isArray()) {
|
||||
int dims = 1;
|
||||
Class baseClass = clazz.getComponentType();
|
||||
while (baseClass.isArray()) {
|
||||
baseClass = baseClass.getComponentType();
|
||||
dims++;
|
||||
}
|
||||
fmtd.append(baseClass.getName());
|
||||
for (int i = 0; i < dims; i++) {
|
||||
fmtd.append("[]");
|
||||
}
|
||||
} else {
|
||||
fmtd.append(clazz.getName());
|
||||
}
|
||||
return fmtd.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
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.
|
||||
*
|
||||
* <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
|
||||
*/
|
||||
public class FunctionReference extends SpelNodeImpl {
|
||||
|
||||
private final String name;
|
||||
|
||||
public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
|
||||
super(pos,arguments);
|
||||
name = functionName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue o = state.lookupVariable(name);
|
||||
if (o == null) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, 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());
|
||||
}
|
||||
try {
|
||||
return executeFunctionJLRMethod(state, (Method) o.getValue());
|
||||
}
|
||||
catch (SpelEvaluationException se) {
|
||||
se.setPosition(getStartPosition());
|
||||
throw se;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a function represented as a java.lang.reflect.Method.
|
||||
*
|
||||
* @param state the expression evaluation state
|
||||
* @param the java method to invoke
|
||||
* @return the return value of the invoked Java method
|
||||
* @throws EvaluationException if there is any problem invoking the method
|
||||
*/
|
||||
private TypedValue executeFunctionJLRMethod(ExpressionState state, Method method) throws EvaluationException {
|
||||
Object[] functionArgs = getArguments(state);
|
||||
|
||||
if (!method.isVarArgs() && method.getParameterTypes().length != functionArgs.length) {
|
||||
throw new SpelEvaluationException(SpelMessage.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION,
|
||||
functionArgs.length, method.getParameterTypes().length);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Convert arguments if necessary and remap them for varargs if required
|
||||
if (functionArgs != null) {
|
||||
TypeConverter converter = state.getEvaluationContext().getTypeConverter();
|
||||
ReflectionHelper.convertAllArguments(converter, functionArgs, method);
|
||||
}
|
||||
if (method.isVarArgs()) {
|
||||
functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs);
|
||||
}
|
||||
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
Object result = method.invoke(method.getClass(), functionArgs);
|
||||
return new TypedValue(result, new TypeDescriptor(new MethodParameter(method,-1)).narrow(result));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.EXCEPTION_DURING_FUNCTION_CALL,
|
||||
this.name, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder("#").append(name);
|
||||
sb.append("(");
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
if (i > 0)
|
||||
sb.append(",");
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// to 'assign' to a function don't use the () suffix and so it is just a variable reference
|
||||
|
||||
/**
|
||||
* Compute the arguments to the function, they are the children of this expression node.
|
||||
* @return an array of argument values for the function call
|
||||
*/
|
||||
private Object[] getArguments(ExpressionState state) throws EvaluationException {
|
||||
// Compute arguments to the function
|
||||
Object[] arguments = new Object[getChildCount()];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
arguments[i] = children[i].getValueInternal(state).getValue();
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,397 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
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
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
// TODO support multidimensional arrays
|
||||
// TODO support correct syntax for multidimensional [][][] and not [,,,]
|
||||
public class Indexer extends SpelNodeImpl {
|
||||
|
||||
// These fields are used when the indexer is being used as a property read accessor. If the name and
|
||||
// target type match these cached values then the cachedReadAccessor is used to read the property.
|
||||
// If they do not match, the correct accessor is discovered and then cached for later use.
|
||||
private String cachedReadName;
|
||||
private Class<?> cachedReadTargetType;
|
||||
private PropertyAccessor cachedReadAccessor;
|
||||
|
||||
// These fields are used when the indexer is being used as a property write accessor. If the name and
|
||||
// target type match these cached values then the cachedWriteAccessor is used to write the property.
|
||||
// If they do not match, the correct accessor is discovered and then cached for later use.
|
||||
private String cachedWriteName;
|
||||
private Class<?> cachedWriteTargetType;
|
||||
private PropertyAccessor cachedWriteAccessor;
|
||||
|
||||
|
||||
public Indexer(int pos, SpelNodeImpl expr) {
|
||||
super(pos, expr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue context = state.getActiveContextObject();
|
||||
Object targetObject = context.getValue();
|
||||
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();
|
||||
TypedValue indexValue = null;
|
||||
Object index = null;
|
||||
|
||||
// This first part of the if clause prevents a 'double dereference' of the property (SPR-5847)
|
||||
if (targetObject instanceof Map && (children[0] instanceof PropertyOrFieldReference)) {
|
||||
PropertyOrFieldReference reference = (PropertyOrFieldReference)children[0];
|
||||
index = reference.getName();
|
||||
indexValue = new TypedValue(index);
|
||||
}
|
||||
else {
|
||||
// In case the map key is unqualified, we want it evaluated against the root object so
|
||||
// temporarily push that on whilst evaluating the key
|
||||
try {
|
||||
state.pushActiveContextObject(state.getRootContextObject());
|
||||
indexValue = children[0].getValueInternal(state);
|
||||
index = indexValue.getValue();
|
||||
}
|
||||
finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
|
||||
// Indexing into a Map
|
||||
if (targetObject instanceof Map) {
|
||||
Object key = index;
|
||||
if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) {
|
||||
key = state.convertValue(key, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
|
||||
}
|
||||
Object value = ((Map<?, ?>) targetObject).get(key);
|
||||
return new TypedValue(value, targetObjectTypeDescriptor.getMapValueTypeDescriptor(value));
|
||||
}
|
||||
|
||||
if (targetObject == null) {
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
|
||||
}
|
||||
|
||||
// if the object is something that looks indexable by an integer, attempt to treat the index value as a number
|
||||
if (targetObject instanceof Collection || targetObject.getClass().isArray() || targetObject instanceof String) {
|
||||
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
|
||||
if (targetObject.getClass().isArray()) {
|
||||
Object arrayElement = accessArrayElement(targetObject, idx);
|
||||
return new TypedValue(arrayElement, targetObjectTypeDescriptor.elementTypeDescriptor(arrayElement));
|
||||
} else if (targetObject instanceof Collection) {
|
||||
Collection c = (Collection) targetObject;
|
||||
if (idx >= c.size()) {
|
||||
if (!growCollection(state, targetObjectTypeDescriptor, idx, c)) {
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
|
||||
}
|
||||
}
|
||||
int pos = 0;
|
||||
for (Object o : c) {
|
||||
if (pos == idx) {
|
||||
return new TypedValue(o, targetObjectTypeDescriptor.elementTypeDescriptor(o));
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
} else if (targetObject instanceof String) {
|
||||
String ctxString = (String) targetObject;
|
||||
if (idx >= ctxString.length()) {
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.STRING_INDEX_OUT_OF_BOUNDS, ctxString.length(), idx);
|
||||
}
|
||||
return new TypedValue(String.valueOf(ctxString.charAt(idx)));
|
||||
}
|
||||
}
|
||||
|
||||
// Try and treat the index value as a property of the context object
|
||||
// TODO could call the conversion service to convert the value to a String
|
||||
if (indexValue.getTypeDescriptor().getType()==String.class) {
|
||||
Class<?> targetObjectRuntimeClass = getObjectClass(targetObject);
|
||||
String name = (String)indexValue.getValue();
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
|
||||
try {
|
||||
if (cachedReadName!=null && cachedReadName.equals(name) && cachedReadTargetType!=null && cachedReadTargetType.equals(targetObjectRuntimeClass)) {
|
||||
// it is OK to use the cached accessor
|
||||
return cachedReadAccessor.read(eContext, targetObject, name);
|
||||
}
|
||||
|
||||
List<PropertyAccessor> accessorsToTry = AstUtils.getPropertyAccessorsToTry(targetObjectRuntimeClass, state);
|
||||
|
||||
if (accessorsToTry != null) {
|
||||
for (PropertyAccessor accessor : accessorsToTry) {
|
||||
if (accessor.canRead(eContext, targetObject, name)) {
|
||||
if (accessor instanceof ReflectivePropertyAccessor) {
|
||||
accessor = ((ReflectivePropertyAccessor)accessor).createOptimalAccessor(eContext, targetObject, name);
|
||||
}
|
||||
this.cachedReadAccessor = accessor;
|
||||
this.cachedReadName = name;
|
||||
this.cachedReadTargetType = targetObjectRuntimeClass;
|
||||
return accessor.read(eContext, targetObject, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (AccessException e) {
|
||||
throw new SpelEvaluationException(getStartPosition(), e, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor.toString());
|
||||
}
|
||||
}
|
||||
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable(ExpressionState expressionState) throws SpelEvaluationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void setValue(ExpressionState state, Object newValue) throws EvaluationException {
|
||||
TypedValue contextObject = state.getActiveContextObject();
|
||||
Object targetObject = contextObject.getValue();
|
||||
TypeDescriptor targetObjectTypeDescriptor = contextObject.getTypeDescriptor();
|
||||
TypedValue index = children[0].getValueInternal(state);
|
||||
|
||||
if (targetObject == null) {
|
||||
throw new SpelEvaluationException(SpelMessage.CANNOT_INDEX_INTO_NULL_VALUE);
|
||||
}
|
||||
// Indexing into a Map
|
||||
if (targetObject instanceof Map) {
|
||||
Map map = (Map) targetObject;
|
||||
Object key = index.getValue();
|
||||
if (targetObjectTypeDescriptor.getMapKeyTypeDescriptor() != null) {
|
||||
key = state.convertValue(index, targetObjectTypeDescriptor.getMapKeyTypeDescriptor());
|
||||
}
|
||||
if (targetObjectTypeDescriptor.getMapValueTypeDescriptor() != null) {
|
||||
newValue = state.convertValue(newValue, targetObjectTypeDescriptor.getMapValueTypeDescriptor());
|
||||
}
|
||||
map.put(key, newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetObjectTypeDescriptor.isArray()) {
|
||||
int idx = (Integer)state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
|
||||
setArrayElement(state, contextObject.getValue(), idx, newValue, targetObjectTypeDescriptor.getElementTypeDescriptor().getType());
|
||||
return;
|
||||
}
|
||||
else if (targetObject instanceof Collection) {
|
||||
int idx = (Integer) state.convertValue(index, TypeDescriptor.valueOf(Integer.class));
|
||||
Collection c = (Collection) targetObject;
|
||||
if (idx >= c.size()) {
|
||||
if (!growCollection(state, targetObjectTypeDescriptor, idx, c)) {
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
|
||||
}
|
||||
}
|
||||
if (targetObject instanceof List) {
|
||||
List list = (List) targetObject;
|
||||
if (targetObjectTypeDescriptor.getElementTypeDescriptor() != null) {
|
||||
newValue = state.convertValue(newValue, targetObjectTypeDescriptor.getElementTypeDescriptor());
|
||||
}
|
||||
list.set(idx, newValue);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Try and treat the index value as a property of the context object
|
||||
// TODO could call the conversion service to convert the value to a String
|
||||
if (index.getTypeDescriptor().getType() == String.class) {
|
||||
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
|
||||
String name = (String)index.getValue();
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
try {
|
||||
if (cachedWriteName!=null && cachedWriteName.equals(name) && cachedWriteTargetType!=null && cachedWriteTargetType.equals(contextObjectClass)) {
|
||||
// it is OK to use the cached accessor
|
||||
cachedWriteAccessor.write(eContext, targetObject, name,newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
List<PropertyAccessor> accessorsToTry = AstUtils.getPropertyAccessorsToTry(contextObjectClass, state);
|
||||
if (accessorsToTry != null) {
|
||||
for (PropertyAccessor accessor : accessorsToTry) {
|
||||
if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
|
||||
this.cachedWriteName = name;
|
||||
this.cachedWriteTargetType = contextObjectClass;
|
||||
this.cachedWriteAccessor = accessor;
|
||||
accessor.write(eContext, contextObject.getValue(), name, newValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (AccessException ae) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
|
||||
name, ae.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE, targetObjectTypeDescriptor.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to grow the specified collection so that the specified index is valid.
|
||||
*
|
||||
* @param state the expression state
|
||||
* @param elementType the type of the elements in the collection
|
||||
* @param index the index into the collection that needs to be valid
|
||||
* @param collection the collection to grow with elements
|
||||
* @return true if collection growing succeeded, otherwise false
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean growCollection(ExpressionState state, TypeDescriptor targetType, int index,
|
||||
Collection collection) {
|
||||
if (state.getConfiguration().isAutoGrowCollections()) {
|
||||
if (targetType.getElementTypeDescriptor() == null) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
|
||||
}
|
||||
TypeDescriptor elementType = targetType.getElementTypeDescriptor();
|
||||
Object newCollectionElement = null;
|
||||
try {
|
||||
int newElements = index - collection.size();
|
||||
while (newElements>0) {
|
||||
collection.add(elementType.getType().newInstance());
|
||||
newElements--;
|
||||
}
|
||||
newCollectionElement = elementType.getType().newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION);
|
||||
}
|
||||
collection.add(newCollectionElement);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
if (i > 0)
|
||||
sb.append(",");
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void setArrayElement(ExpressionState state, Object ctx, int idx, Object newValue, Class clazz) throws EvaluationException {
|
||||
Class<?> arrayComponentType = clazz;
|
||||
if (arrayComponentType == Integer.TYPE) {
|
||||
int[] array = (int[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Integer)state.convertValue(newValue, TypeDescriptor.valueOf(Integer.class));
|
||||
} else if (arrayComponentType == Boolean.TYPE) {
|
||||
boolean[] array = (boolean[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Boolean)state.convertValue(newValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
} else if (arrayComponentType == Character.TYPE) {
|
||||
char[] array = (char[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Character)state.convertValue(newValue, TypeDescriptor.valueOf(Character.class));
|
||||
} else if (arrayComponentType == Long.TYPE) {
|
||||
long[] array = (long[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Long)state.convertValue(newValue, TypeDescriptor.valueOf(Long.class));
|
||||
} else if (arrayComponentType == Short.TYPE) {
|
||||
short[] array = (short[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Short)state.convertValue(newValue, TypeDescriptor.valueOf(Short.class));
|
||||
} else if (arrayComponentType == Double.TYPE) {
|
||||
double[] array = (double[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Double)state.convertValue(newValue, TypeDescriptor.valueOf(Double.class));
|
||||
} else if (arrayComponentType == Float.TYPE) {
|
||||
float[] array = (float[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Float)state.convertValue(newValue, TypeDescriptor.valueOf(Float.class));
|
||||
} else if (arrayComponentType == Byte.TYPE) {
|
||||
byte[] array = (byte[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = (Byte)state.convertValue(newValue, TypeDescriptor.valueOf(Byte.class));
|
||||
} else {
|
||||
Object[] array = (Object[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
array[idx] = state.convertValue(newValue, TypeDescriptor.valueOf(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
private Object accessArrayElement(Object ctx, int idx) throws SpelEvaluationException {
|
||||
Class<?> arrayComponentType = ctx.getClass().getComponentType();
|
||||
if (arrayComponentType == Integer.TYPE) {
|
||||
int[] array = (int[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else if (arrayComponentType == Boolean.TYPE) {
|
||||
boolean[] array = (boolean[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else if (arrayComponentType == Character.TYPE) {
|
||||
char[] array = (char[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else if (arrayComponentType == Long.TYPE) {
|
||||
long[] array = (long[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else if (arrayComponentType == Short.TYPE) {
|
||||
short[] array = (short[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else if (arrayComponentType == Double.TYPE) {
|
||||
double[] array = (double[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else if (arrayComponentType == Float.TYPE) {
|
||||
float[] array = (float[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else if (arrayComponentType == Byte.TYPE) {
|
||||
byte[] array = (byte[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
} else {
|
||||
Object[] array = (Object[]) ctx;
|
||||
checkAccess(array.length, idx);
|
||||
return array[idx];
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAccess(int arrayLength, int index) throws SpelEvaluationException {
|
||||
if (index > arrayLength) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.ARRAY_INDEX_OUT_OF_BOUNDS, arrayLength, index);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
|
||||
/**
|
||||
* Represent a list in an expression, e.g. '{1,2,3}'
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0.4
|
||||
*/
|
||||
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
|
||||
* created.
|
||||
*/
|
||||
private void checkIfConstant() {
|
||||
boolean isConstant = true;
|
||||
for (int c = 0, max = getChildCount(); c < max; c++) {
|
||||
SpelNode child = getChild(c);
|
||||
if (!(child instanceof Literal)) {
|
||||
if (child instanceof InlineList) {
|
||||
InlineList inlineList = (InlineList) child;
|
||||
if (!inlineList.isConstant()) {
|
||||
isConstant = false;
|
||||
}
|
||||
} else {
|
||||
isConstant = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isConstant) {
|
||||
List<Object> constantList = new ArrayList<Object>();
|
||||
int childcount = getChildCount();
|
||||
for (int c = 0; c < childcount; c++) {
|
||||
SpelNode child = getChild(c);
|
||||
if ((child instanceof Literal)) {
|
||||
constantList.add(((Literal) child).getLiteralValue().getValue());
|
||||
} else if (child instanceof InlineList) {
|
||||
constantList.add(((InlineList) child).getConstantValue());
|
||||
}
|
||||
}
|
||||
this.constant = new TypedValue(Collections.unmodifiableList(constantList));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException {
|
||||
if (constant != null) {
|
||||
return constant;
|
||||
} else {
|
||||
List<Object> returnValue = new ArrayList<Object>();
|
||||
int childcount = getChildCount();
|
||||
for (int c = 0; c < childcount; c++) {
|
||||
returnValue.add(getChild(c).getValue(expressionState));
|
||||
}
|
||||
return new TypedValue(returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
// string ast matches input string, not the 'toString()' of the resultant collection, which would use []
|
||||
s.append('{');
|
||||
int count = getChildCount();
|
||||
for (int c = 0; c < count; c++) {
|
||||
if (c > 0) {
|
||||
s.append(',');
|
||||
}
|
||||
s.append(getChild(c).toStringAST());
|
||||
}
|
||||
s.append('}');
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this list is a constant value
|
||||
*/
|
||||
public boolean isConstant() {
|
||||
return constant != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Object> getConstantValue() {
|
||||
return (List<Object>) constant.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents an integer literal.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelParseException;
|
||||
import org.springframework.expression.spel.InternalParseException;
|
||||
|
||||
/**
|
||||
* Common superclass for nodes representing literals (boolean, string, number, etc).
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public abstract class Literal extends SpelNodeImpl {
|
||||
|
||||
protected String literalValue;
|
||||
|
||||
public Literal(String payload, int pos) {
|
||||
super(pos);
|
||||
this.literalValue = payload;
|
||||
}
|
||||
|
||||
public abstract TypedValue getLiteralValue();
|
||||
|
||||
@Override
|
||||
public final TypedValue getValueInternal(ExpressionState state) throws SpelEvaluationException {
|
||||
return getLiteralValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getLiteralValue().getValue().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
return toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the string form of a number, using the specified base if supplied and return an appropriate literal to
|
||||
* hold it. Any suffix to indicate a long will be taken into account (either 'l' or 'L' is supported).
|
||||
*
|
||||
* @param numberToken the token holding the number as its payload (eg. 1234 or 0xCAFE)
|
||||
* @param radix the base of number
|
||||
* @return a subtype of Literal that can represent it
|
||||
*/
|
||||
public static Literal getIntLiteral(String numberToken, int pos, int radix) {
|
||||
try {
|
||||
int value = Integer.parseInt(numberToken, radix);
|
||||
return new IntLiteral(numberToken, pos, value);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_AN_INTEGER, numberToken));
|
||||
}
|
||||
}
|
||||
|
||||
public static Literal getLongLiteral(String numberToken, int pos, int radix) {
|
||||
try {
|
||||
long value = Long.parseLong(numberToken, radix);
|
||||
return new LongLiteral(numberToken, pos, value);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_LONG, numberToken));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO should allow for 'f' for float, not just double
|
||||
public static Literal getRealLiteral(String numberToken, int pos, boolean isFloat) {
|
||||
try {
|
||||
if (isFloat) {
|
||||
float value = Float.parseFloat(numberToken);
|
||||
return new RealLiteral(numberToken, pos, value);
|
||||
} else {
|
||||
double value = Double.parseDouble(numberToken);
|
||||
return new RealLiteral(numberToken, pos, value);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new InternalParseException(new SpelParseException(pos>>16, nfe, SpelMessage.NOT_A_REAL, numberToken));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
/**
|
||||
* Expression language AST node that represents a long integer literal.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.ExpressionInvocationTargetException;
|
||||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.MethodResolver;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class MethodReference extends SpelNodeImpl {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final boolean nullSafe;
|
||||
|
||||
private volatile MethodExecutor cachedExecutor;
|
||||
|
||||
|
||||
public MethodReference(boolean nullSafe, String methodName, int pos, SpelNodeImpl... arguments) {
|
||||
super(pos,arguments);
|
||||
this.name = methodName;
|
||||
this.nullSafe = nullSafe;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue currentContext = state.getActiveContextObject();
|
||||
Object[] arguments = new Object[getChildCount()];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
// Make the root object the active context again for evaluating the parameter
|
||||
// expressions
|
||||
try {
|
||||
state.pushActiveContextObject(state.getRootContextObject());
|
||||
arguments[i] = children[i].getValueInternal(state).getValue();
|
||||
}
|
||||
finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
if (currentContext.getValue() == null) {
|
||||
if (this.nullSafe) {
|
||||
return TypedValue.NULL;
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED,
|
||||
FormatHelper.formatMethodForMessage(name, getTypes(arguments)));
|
||||
}
|
||||
}
|
||||
|
||||
MethodExecutor executorToUse = this.cachedExecutor;
|
||||
if (executorToUse != null) {
|
||||
try {
|
||||
return executorToUse.execute(
|
||||
state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
// Two reasons this can occur:
|
||||
// 1. the method invoked actually threw a real exception
|
||||
// 2. the method invoked was not passed the arguments it expected and has become 'stale'
|
||||
|
||||
// In the first case we should not retry, in the second case we should see if there is a
|
||||
// better suited method.
|
||||
|
||||
// To determine which situation it is, the AccessException will contain a cause.
|
||||
// If the cause is an InvocationTargetException, a user exception was thrown inside the method.
|
||||
// Otherwise the method could not be invoked.
|
||||
throwSimpleExceptionIfPossible(state, ae);
|
||||
|
||||
// at this point we know it wasn't a user problem so worth a retry if a better candidate can be found
|
||||
this.cachedExecutor = null;
|
||||
}
|
||||
}
|
||||
|
||||
// either there was no accessor or it no longer existed
|
||||
executorToUse = findAccessorForMethod(this.name, getTypes(arguments), state);
|
||||
this.cachedExecutor = executorToUse;
|
||||
try {
|
||||
return executorToUse.execute(
|
||||
state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
|
||||
} catch (AccessException ae) {
|
||||
// Same unwrapping exception handling as above in above catch block
|
||||
throwSimpleExceptionIfPossible(state, ae);
|
||||
throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION,
|
||||
this.name, state.getActiveContextObject().getValue().getClass().getName(), ae.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode the AccessException, throwing a lightweight evaluation exception or, if the cause was a RuntimeException,
|
||||
* throw the RuntimeException directly.
|
||||
*/
|
||||
private void throwSimpleExceptionIfPossible(ExpressionState state, AccessException ae) {
|
||||
if (ae.getCause() instanceof InvocationTargetException) {
|
||||
Throwable rootCause = ae.getCause().getCause();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<TypeDescriptor> getTypes(Object... arguments) {
|
||||
List<TypeDescriptor> descriptors = new ArrayList<TypeDescriptor>(arguments.length);
|
||||
for (Object argument : arguments) {
|
||||
descriptors.add(TypeDescriptor.forObject(argument));
|
||||
}
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(name).append("(");
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
if (i > 0)
|
||||
sb.append(",");
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private MethodExecutor findAccessorForMethod(String name, List<TypeDescriptor> argumentTypes, ExpressionState state)
|
||||
throws SpelEvaluationException {
|
||||
|
||||
TypedValue context = state.getActiveContextObject();
|
||||
Object contextObject = context.getValue();
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
|
||||
List<MethodResolver> mResolvers = eContext.getMethodResolvers();
|
||||
if (mResolvers != null) {
|
||||
for (MethodResolver methodResolver : mResolvers) {
|
||||
try {
|
||||
MethodExecutor cEx = methodResolver.resolve(
|
||||
state.getEvaluationContext(), contextObject, name, argumentTypes);
|
||||
if (cEx != null) {
|
||||
return cEx;
|
||||
}
|
||||
}
|
||||
catch (AccessException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(),ex, SpelMessage.PROBLEM_LOCATING_METHOD, name, contextObject.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new SpelEvaluationException(getStartPosition(),SpelMessage.METHOD_NOT_FOUND, FormatHelper.formatMethodForMessage(name, argumentTypes),
|
||||
FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class<?>) contextObject) : contextObject.getClass()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class NullLiteral extends Literal {
|
||||
|
||||
public NullLiteral(int pos) {
|
||||
super(null,pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getLiteralValue() {
|
||||
return TypedValue.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Represents the boolean AND operation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpAnd extends Operator {
|
||||
|
||||
public OpAnd(int pos, SpelNodeImpl... operands) {
|
||||
super("and", pos, operands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
boolean leftValue;
|
||||
boolean rightValue;
|
||||
|
||||
try {
|
||||
TypedValue typedValue = getLeftOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
leftValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
}
|
||||
catch (SpelEvaluationException ee) {
|
||||
ee.setPosition(getLeftOperand().getStartPosition());
|
||||
throw ee;
|
||||
}
|
||||
|
||||
if (leftValue == false) {
|
||||
return BooleanTypedValue.forValue(false); // no need to evaluate right operand
|
||||
}
|
||||
|
||||
try {
|
||||
TypedValue typedValue = getRightOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
rightValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
}
|
||||
catch (SpelEvaluationException ee) {
|
||||
ee.setPosition(getRightOperand().getStartPosition());
|
||||
throw ee;
|
||||
}
|
||||
|
||||
return /* leftValue && */BooleanTypedValue.forValue(rightValue);
|
||||
}
|
||||
|
||||
private void assertTypedValueNotNull(TypedValue typedValue) {
|
||||
if (TypedValue.NULL.equals(typedValue)) {
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* Implements division operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpDivide extends Operator {
|
||||
|
||||
public OpDivide(int pos, SpelNodeImpl... operands) {
|
||||
super("/", pos, operands);
|
||||
}
|
||||
|
||||
@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 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 Long || op2 instanceof Long) {
|
||||
return new TypedValue(op1.longValue() / op2.longValue());
|
||||
} else { // TODO what about non-int result of the division?
|
||||
return new TypedValue(op1.intValue() / op2.intValue());
|
||||
}
|
||||
}
|
||||
Object result = state.operate(Operation.DIVIDE, operandOne, operandTwo);
|
||||
return new TypedValue(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Implements equality operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpEQ extends Operator {
|
||||
|
||||
public OpEQ(int pos, SpelNodeImpl... operands) {
|
||||
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 Long || op2 instanceof Long) {
|
||||
return BooleanTypedValue.forValue(op1.longValue() == op2.longValue());
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Implements greater-than-or-equal operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpGE extends Operator {
|
||||
|
||||
public OpGE(int pos, SpelNodeImpl... operands) {
|
||||
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 leftNumber = (Number) left;
|
||||
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) {
|
||||
return BooleanTypedValue.forValue( leftNumber.longValue() >= rightNumber.longValue());
|
||||
} else {
|
||||
return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue());
|
||||
}
|
||||
}
|
||||
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) >= 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Implements greater-than operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpGT extends Operator {
|
||||
|
||||
public OpGT(int pos, SpelNodeImpl... operands) {
|
||||
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 leftNumber = (Number) left;
|
||||
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) {
|
||||
return BooleanTypedValue.forValue(leftNumber.longValue() > rightNumber.longValue());
|
||||
} else {
|
||||
return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue());
|
||||
}
|
||||
}
|
||||
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) > 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Implements the less-than-or-equal operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpLE extends Operator {
|
||||
|
||||
public OpLE(int pos, SpelNodeImpl... operands) {
|
||||
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 leftNumber = (Number) left;
|
||||
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) {
|
||||
return BooleanTypedValue.forValue(leftNumber.longValue() <= rightNumber.longValue());
|
||||
} else {
|
||||
return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue());
|
||||
}
|
||||
}
|
||||
return BooleanTypedValue.forValue( state.getTypeComparator().compare(left, right) <= 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Implements the less-than operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpLT extends Operator {
|
||||
|
||||
public OpLT(int pos, SpelNodeImpl... operands) {
|
||||
super("<", pos, operands);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
if (left instanceof Number && right instanceof Number) {
|
||||
Number leftNumber = (Number) left;
|
||||
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) {
|
||||
return BooleanTypedValue.forValue(leftNumber.longValue() < rightNumber.longValue());
|
||||
} else {
|
||||
return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue());
|
||||
}
|
||||
}
|
||||
return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) < 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* The minus operator supports:
|
||||
* <ul>
|
||||
* <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'
|
||||
* </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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpMinus extends Operator {
|
||||
|
||||
public OpMinus(int pos, SpelNodeImpl... operands) {
|
||||
super("-", pos, 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 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 Long) {
|
||||
return new TypedValue(0 - n.longValue());
|
||||
} else {
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
if (getRightOperand() == null) { // unary minus
|
||||
return new StringBuilder().append("-").append(getLeftOperand().toStringAST()).toString();
|
||||
}
|
||||
return super.toStringAST();
|
||||
}
|
||||
public SpelNodeImpl getRightOperand() {
|
||||
if (children.length<2) {return null;}
|
||||
return children[1];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* Implements the modulus operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpModulus extends Operator {
|
||||
|
||||
public OpModulus(int pos, SpelNodeImpl... operands) {
|
||||
super("%", pos, operands);
|
||||
}
|
||||
|
||||
@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 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 Long || op2 instanceof Long) {
|
||||
return new TypedValue(op1.longValue() % op2.longValue());
|
||||
} else {
|
||||
return new TypedValue(op1.intValue() % op2.intValue());
|
||||
}
|
||||
}
|
||||
return state.operate(Operation.MODULUS, operandOne, operandTwo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* Implements the multiply operator. Conversions and promotions:
|
||||
* http://java.sun.com/docs/books/jls/third_edition/html/conversions.html Section 5.6.2:
|
||||
*
|
||||
* <p>If any of the operands is of a reference type, unboxing conversion (<28>5.1.8) is performed. Then:<br>
|
||||
* If either operand is of type double, the other is converted to double.<br>
|
||||
* Otherwise, if either operand is of type float, the other is converted to float.<br>
|
||||
* Otherwise, if either operand is of type long, the other is converted to long.<br>
|
||||
* Otherwise, both operands are converted to type int.
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpMultiply extends Operator {
|
||||
|
||||
|
||||
public OpMultiply(int pos, SpelNodeImpl... operands) {
|
||||
super("*", pos, operands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements multiply directly here for some types of operand, otherwise delegates to any registered overloader for
|
||||
* types it does not recognize. Supported types here are:
|
||||
* <ul>
|
||||
* <li>integers
|
||||
* <li>doubles
|
||||
* <li>string and int ('abc' * 2 == 'abcabc')
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public 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());
|
||||
} else if (leftNumber instanceof Long || rightNumber instanceof Long) {
|
||||
return new TypedValue(leftNumber.longValue() * rightNumber.longValue());
|
||||
} else {
|
||||
return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
|
||||
}
|
||||
} else if (operandOne instanceof String && operandTwo instanceof Integer) {
|
||||
int repeats = (Integer) operandTwo;
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < repeats; i++) {
|
||||
result.append(operandOne);
|
||||
}
|
||||
return new TypedValue(result.toString());
|
||||
}
|
||||
return state.operate(Operation.MULTIPLY, operandOne, operandTwo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Implements the not-equal operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpNE extends Operator {
|
||||
|
||||
public OpNE(int pos, SpelNodeImpl... operands) {
|
||||
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 Long || op2 instanceof Long) {
|
||||
return BooleanTypedValue.forValue(op1.longValue() != op2.longValue());
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Represents the boolean OR operation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpOr extends Operator {
|
||||
|
||||
public OpOr(int pos, SpelNodeImpl... operands) {
|
||||
super("or", pos, operands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
boolean leftValue;
|
||||
boolean rightValue;
|
||||
try {
|
||||
TypedValue typedValue = getLeftOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
leftValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
}
|
||||
catch (SpelEvaluationException see) {
|
||||
see.setPosition(getLeftOperand().getStartPosition());
|
||||
throw see;
|
||||
}
|
||||
|
||||
if (leftValue == true) {
|
||||
return BooleanTypedValue.TRUE; // no need to evaluate right operand
|
||||
}
|
||||
|
||||
try {
|
||||
TypedValue typedValue = getRightOperand().getValueInternal(state);
|
||||
this.assertTypedValueNotNull(typedValue);
|
||||
rightValue = (Boolean)state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
}
|
||||
catch (SpelEvaluationException see) {
|
||||
see.setPosition(getRightOperand().getStartPosition()); // TODO end positions here and in similar situations
|
||||
throw see;
|
||||
}
|
||||
|
||||
return BooleanTypedValue.forValue(leftValue || rightValue);
|
||||
}
|
||||
|
||||
private void assertTypedValueNotNull(TypedValue typedValue) {
|
||||
if (TypedValue.NULL.equals(typedValue)) {
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* The plus operator will:
|
||||
* <ul>
|
||||
* <li>add doubles (floats are represented as doubles)
|
||||
* <li>add longs
|
||||
* <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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpPlus extends Operator {
|
||||
|
||||
public OpPlus(int pos, SpelNodeImpl... operands) {
|
||||
super("+", pos, 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) {
|
||||
return new TypedValue(((Double) operandOne).doubleValue());
|
||||
} else if (operandOne instanceof Long) {
|
||||
return new TypedValue(((Long) operandOne).longValue());
|
||||
} else {
|
||||
return new TypedValue(((Integer) operandOne).intValue());
|
||||
}
|
||||
}
|
||||
return state.operate(Operation.ADD, operandOne, null);
|
||||
}
|
||||
else {
|
||||
Object operandOne = leftOp.getValueInternal(state).getValue();
|
||||
Object operandTwo = rightOp.getValueInternal(state).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 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":operandTwo.toString()));
|
||||
return new TypedValue(result.toString());
|
||||
} else if (operandTwo instanceof String) {
|
||||
StringBuilder result = new StringBuilder((operandOne==null?"null":operandOne.toString()));
|
||||
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
|
||||
return new StringBuilder().append("+").append(getLeftOperand().toStringAST()).toString();
|
||||
}
|
||||
return super.toStringAST();
|
||||
}
|
||||
|
||||
public SpelNodeImpl getRightOperand() {
|
||||
if (children.length<2) {return null;}
|
||||
return children[1];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
||||
public SpelNodeImpl getRightOperand() {
|
||||
return children[1];
|
||||
}
|
||||
|
||||
public final String getOperatorName() {
|
||||
return operatorName;
|
||||
}
|
||||
|
||||
/**
|
||||
* String format for all operators is the same '(' [operand] [operator] [operand] ')'
|
||||
*/
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
sb.append(getChild(0).toStringAST());
|
||||
for (int i = 1; i < getChildCount(); i++) {
|
||||
sb.append(" ").append(getOperatorName()).append(" ");
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeComparator;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorBetween extends Operator {
|
||||
|
||||
public OperatorBetween(int pos, SpelNodeImpl... operands) {
|
||||
super("between", pos, operands);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@Override
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
Object left = getLeftOperand().getValueInternal(state).getValue();
|
||||
Object right = getRightOperand().getValueInternal(state).getValue();
|
||||
if (!(right instanceof List) || ((List<?>) right).size() != 2) {
|
||||
throw new SpelEvaluationException(getRightOperand().getStartPosition(),
|
||||
SpelMessage.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST);
|
||||
}
|
||||
List<?> l = (List<?>) right;
|
||||
Object low = l.get(0);
|
||||
Object high = l.get(1);
|
||||
TypeComparator comparator = state.getTypeComparator();
|
||||
try {
|
||||
return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 && comparator.compare(left, high) <= 0));
|
||||
} catch (SpelEvaluationException ex) {
|
||||
ex.setPosition(getStartPosition());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
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</code> does in Java.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorInstanceof extends Operator {
|
||||
|
||||
public OperatorInstanceof(int pos, SpelNodeImpl... operands) {
|
||||
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.
|
||||
* @param state the expression state
|
||||
* @return true if the left operand is an instanceof of the right operand, otherwise false
|
||||
* @throws EvaluationException if there is a problem evaluating the expression
|
||||
*/
|
||||
@Override
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue left = getLeftOperand().getValueInternal(state);
|
||||
TypedValue right = getRightOperand().getValueInternal(state);
|
||||
Object leftValue = left.getValue();
|
||||
Object rightValue = right.getValue();
|
||||
if (leftValue == null) {
|
||||
return BooleanTypedValue.FALSE; // null is not an instanceof anything
|
||||
}
|
||||
if (rightValue == null || !(rightValue instanceof Class<?>)) {
|
||||
throw new SpelEvaluationException(getRightOperand().getStartPosition(),
|
||||
SpelMessage.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND,
|
||||
(rightValue == null ? "null" : rightValue.getClass().getName()));
|
||||
}
|
||||
Class<?> rightClass = (Class<?>) rightValue;
|
||||
return BooleanTypedValue.forValue(rightClass.isAssignableFrom(leftValue.getClass()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorMatches extends Operator {
|
||||
|
||||
public OperatorMatches(int pos, SpelNodeImpl... operands) {
|
||||
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)
|
||||
*/
|
||||
@Override
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
SpelNodeImpl leftOp = getLeftOperand();
|
||||
SpelNodeImpl rightOp = getRightOperand();
|
||||
Object left = leftOp.getValue(state, String.class);
|
||||
Object right = getRightOperand().getValueInternal(state).getValue();
|
||||
try {
|
||||
if (!(left instanceof String)) {
|
||||
throw new SpelEvaluationException(leftOp.getStartPosition(),
|
||||
SpelMessage.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left);
|
||||
}
|
||||
if (!(right instanceof String)) {
|
||||
throw new SpelEvaluationException(rightOp.getStartPosition(),
|
||||
SpelMessage.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right);
|
||||
}
|
||||
Pattern pattern = Pattern.compile((String) right);
|
||||
Matcher matcher = pattern.matcher((String) left);
|
||||
return BooleanTypedValue.forValue(matcher.matches());
|
||||
}
|
||||
catch (PatternSyntaxException pse) {
|
||||
throw new SpelEvaluationException(rightOp.getStartPosition(), pse, SpelMessage.INVALID_PATTERN, right);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.support.BooleanTypedValue;
|
||||
|
||||
/**
|
||||
* Represents a NOT operation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do not extend BinaryOperator
|
||||
|
||||
public OperatorNot(int pos, SpelNodeImpl operand) {
|
||||
super(pos, operand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
try {
|
||||
TypedValue typedValue = children[0].getValueInternal(state);
|
||||
if (TypedValue.NULL.equals(typedValue)) {
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
|
||||
}
|
||||
boolean value = (Boolean) state.convertValue(typedValue, TypeDescriptor.valueOf(Boolean.class));
|
||||
return BooleanTypedValue.forValue(!value);
|
||||
}
|
||||
catch (SpelEvaluationException see) {
|
||||
see.setPosition(getChild(0).getStartPosition());
|
||||
throw see;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("!").append(getChild(0).toStringAST());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* The power operator.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OperatorPower extends Operator {
|
||||
|
||||
public OperatorPower(int pos, SpelNodeImpl... operands) {
|
||||
super("^", pos, operands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
SpelNodeImpl leftOp = getLeftOperand();
|
||||
SpelNodeImpl rightOp = getRightOperand();
|
||||
|
||||
Object operandOne = leftOp.getValueInternal(state).getValue();
|
||||
Object operandTwo = rightOp.getValueInternal(state).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(Math.pow(op1.doubleValue(),op2.doubleValue()));
|
||||
} 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 state.operate(Operation.POWER, operandOne, operandTwo);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
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:
|
||||
* "{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
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Projection extends SpelNodeImpl {
|
||||
|
||||
private final boolean nullSafe;
|
||||
|
||||
public Projection(boolean nullSafe, int pos, SpelNodeImpl expression) {
|
||||
super(pos, expression);
|
||||
this.nullSafe = nullSafe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue op = state.getActiveContextObject();
|
||||
|
||||
Object operand = op.getValue();
|
||||
boolean operandIsArray = ObjectUtils.isArray(operand);
|
||||
// TypeDescriptor operandTypeDescriptor = op.getTypeDescriptor();
|
||||
|
||||
// When the input is a map, we push a special context object on the stack
|
||||
// before calling the specified operation. This special context object
|
||||
// has two fields 'key' and 'value' that refer to the map entries key
|
||||
// and value, and they can be referenced in the operation
|
||||
// eg. {'a':'y','b':'n'}.!{value=='y'?key:null}" == ['a', null]
|
||||
if (operand instanceof Map) {
|
||||
Map<?, ?> mapData = (Map<?, ?>) operand;
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
for (Map.Entry entry : mapData.entrySet()) {
|
||||
try {
|
||||
state.pushActiveContextObject(new TypedValue(entry));
|
||||
result.add(this.children[0].getValueInternal(state).getValue());
|
||||
}
|
||||
finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
return new TypedValue(result); // TODO unable to build correct type descriptor
|
||||
}
|
||||
else if (operand instanceof Collection || operandIsArray) {
|
||||
Collection<?> data = (operand instanceof Collection ? (Collection<?>) operand :
|
||||
Arrays.asList(ObjectUtils.toObjectArray(operand)));
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
int idx = 0;
|
||||
Class<?> arrayElementType = null;
|
||||
for (Object element : data) {
|
||||
try {
|
||||
state.pushActiveContextObject(new TypedValue(element));
|
||||
state.enterScope("index", idx);
|
||||
Object value = children[0].getValueInternal(state).getValue();
|
||||
if (value != null && operandIsArray) {
|
||||
arrayElementType = determineCommonType(arrayElementType, value.getClass());
|
||||
}
|
||||
result.add(value);
|
||||
}
|
||||
finally {
|
||||
state.exitScope();
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
if (operandIsArray) {
|
||||
if (arrayElementType == null) {
|
||||
arrayElementType = Object.class;
|
||||
}
|
||||
Object resultArray = Array.newInstance(arrayElementType, result.size());
|
||||
System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
|
||||
return new TypedValue(resultArray);
|
||||
}
|
||||
return new TypedValue(result);
|
||||
}
|
||||
else {
|
||||
if (operand==null) {
|
||||
if (this.nullSafe) {
|
||||
return TypedValue.NULL;
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
return sb.append("![").append(getChild(0).toStringAST()).append("]").toString();
|
||||
}
|
||||
|
||||
private Class<?> determineCommonType(Class<?> oldType, Class<?> newType) {
|
||||
if (oldType == null) {
|
||||
return newType;
|
||||
}
|
||||
if (oldType.isAssignableFrom(newType)) {
|
||||
return oldType;
|
||||
}
|
||||
Class<?> nextType = newType;
|
||||
while (nextType != Object.class) {
|
||||
if (nextType.isAssignableFrom(oldType)) {
|
||||
return nextType;
|
||||
}
|
||||
nextType = nextType.getSuperclass();
|
||||
}
|
||||
Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(newType);
|
||||
for (Class<?> nextInterface : interfaces) {
|
||||
if (nextInterface.isAssignableFrom(oldType)) {
|
||||
return nextInterface;
|
||||
}
|
||||
}
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
||||
|
||||
/**
|
||||
* Represents a simple property or field reference.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Clark Duplichien
|
||||
* @since 3.0
|
||||
*/
|
||||
public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
|
||||
private final boolean nullSafe;
|
||||
|
||||
private final String name;
|
||||
|
||||
private volatile PropertyAccessor cachedReadAccessor;
|
||||
|
||||
private volatile PropertyAccessor cachedWriteAccessor;
|
||||
|
||||
|
||||
public PropertyOrFieldReference(boolean nullSafe, String propertyOrFieldName, int pos) {
|
||||
super(pos);
|
||||
this.nullSafe = nullSafe;
|
||||
this.name = propertyOrFieldName;
|
||||
}
|
||||
|
||||
|
||||
public boolean isNullSafe() {
|
||||
return this.nullSafe;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue result = readProperty(state, this.name);
|
||||
|
||||
// Dynamically create the objects if the user has requested that optional behaviour
|
||||
if (result.getValue() == null && state.getConfiguration().isAutoGrowNullReferences() &&
|
||||
nextChildIs(Indexer.class, PropertyOrFieldReference.class)) {
|
||||
TypeDescriptor resultDescriptor = result.getTypeDescriptor();
|
||||
// Creating lists and maps
|
||||
if ((resultDescriptor.getType().equals(List.class) || resultDescriptor.getType().equals(Map.class))) {
|
||||
// Create a new collection or map ready for the indexer
|
||||
if (resultDescriptor.getType().equals(List.class)) {
|
||||
try {
|
||||
if (isWritable(state)) {
|
||||
List newList = ArrayList.class.newInstance();
|
||||
writeProperty(state, this.name, newList);
|
||||
result = readProperty(state, this.name);
|
||||
}
|
||||
}
|
||||
catch (InstantiationException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||
SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||
SpelMessage.UNABLE_TO_CREATE_LIST_FOR_INDEXING);
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
if (isWritable(state)) {
|
||||
Map newMap = HashMap.class.newInstance();
|
||||
writeProperty(state, name, newMap);
|
||||
result = readProperty(state, this.name);
|
||||
}
|
||||
}
|
||||
catch (InstantiationException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||
SpelMessage.UNABLE_TO_CREATE_MAP_FOR_INDEXING);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||
SpelMessage.UNABLE_TO_CREATE_MAP_FOR_INDEXING);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 'simple' object
|
||||
try {
|
||||
if (isWritable(state)) {
|
||||
Object newObject = result.getTypeDescriptor().getType().newInstance();
|
||||
writeProperty(state, name, newObject);
|
||||
result = readProperty(state, this.name);
|
||||
}
|
||||
}
|
||||
catch (InstantiationException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||
SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, result.getTypeDescriptor().getType());
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex,
|
||||
SpelMessage.UNABLE_TO_DYNAMICALLY_CREATE_OBJECT, result.getTypeDescriptor().getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ExpressionState state, Object newValue) throws SpelEvaluationException {
|
||||
writeProperty(state, this.name, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable(ExpressionState state) throws SpelEvaluationException {
|
||||
return isWritableProperty(this.name, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to read the named property from the current context object.
|
||||
* @param state the evaluation state
|
||||
* @param name the name of the property
|
||||
* @return the value of the property
|
||||
* @throws SpelEvaluationException if any problem accessing the property or it cannot be found
|
||||
*/
|
||||
private TypedValue readProperty(ExpressionState state, String name) throws EvaluationException {
|
||||
TypedValue contextObject = state.getActiveContextObject();
|
||||
Object targetObject = contextObject.getValue();
|
||||
|
||||
if (targetObject == null && this.nullSafe) {
|
||||
return TypedValue.NULL;
|
||||
}
|
||||
|
||||
PropertyAccessor accessorToUse = this.cachedReadAccessor;
|
||||
if (accessorToUse != null) {
|
||||
try {
|
||||
return accessorToUse.read(state.getEvaluationContext(), contextObject.getValue(), name);
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
// this is OK - it may have gone stale due to a class change,
|
||||
// let's try to get a new one and call it before giving up
|
||||
this.cachedReadAccessor = null;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
|
||||
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
|
||||
// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
|
||||
// get the accessor and use it. If they are not cacheable but report they can read the property
|
||||
// then ask them to read it
|
||||
if (accessorsToTry != null) {
|
||||
try {
|
||||
for (PropertyAccessor accessor : accessorsToTry) {
|
||||
if (accessor.canRead(eContext, contextObject.getValue(), name)) {
|
||||
if (accessor instanceof ReflectivePropertyAccessor) {
|
||||
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
|
||||
eContext, contextObject.getValue(), name);
|
||||
}
|
||||
this.cachedReadAccessor = accessor;
|
||||
return accessor.read(eContext, contextObject.getValue(), name);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
throw new SpelEvaluationException(ae, SpelMessage.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage());
|
||||
}
|
||||
}
|
||||
if (contextObject.getValue() == null) {
|
||||
throw new SpelEvaluationException(SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL, name);
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_READABLE, name,
|
||||
FormatHelper.formatClassNameForMessage(contextObjectClass));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeProperty(ExpressionState state, String name, Object newValue) throws SpelEvaluationException {
|
||||
TypedValue contextObject = state.getActiveContextObject();
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
|
||||
if (contextObject.getValue() == null && nullSafe) {
|
||||
return;
|
||||
}
|
||||
|
||||
PropertyAccessor accessorToUse = this.cachedWriteAccessor;
|
||||
if (accessorToUse != null) {
|
||||
try {
|
||||
accessorToUse.write(state.getEvaluationContext(), contextObject.getValue(), name, newValue);
|
||||
return;
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
// this is OK - it may have gone stale due to a class change,
|
||||
// let's try to get a new one and call it before giving up
|
||||
this.cachedWriteAccessor = null;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> contextObjectClass = getObjectClass(contextObject.getValue());
|
||||
|
||||
List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
|
||||
if (accessorsToTry != null) {
|
||||
try {
|
||||
for (PropertyAccessor accessor : accessorsToTry) {
|
||||
if (accessor.canWrite(eContext, contextObject.getValue(), name)) {
|
||||
this.cachedWriteAccessor = accessor;
|
||||
accessor.write(eContext, contextObject.getValue(), name, newValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_PROPERTY_WRITE,
|
||||
name, ae.getMessage());
|
||||
}
|
||||
}
|
||||
if (contextObject.getValue()==null) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL, name);
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.PROPERTY_OR_FIELD_NOT_WRITABLE, name,
|
||||
FormatHelper.formatClassNameForMessage(contextObjectClass));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWritableProperty(String name, ExpressionState state) throws SpelEvaluationException {
|
||||
Object contextObject = state.getActiveContextObject().getValue();
|
||||
// TypeDescriptor td = state.getActiveContextObject().getTypeDescriptor();
|
||||
EvaluationContext eContext = state.getEvaluationContext();
|
||||
List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(getObjectClass(contextObject), state);
|
||||
if (resolversToTry != null) {
|
||||
for (PropertyAccessor pfResolver : resolversToTry) {
|
||||
try {
|
||||
if (pfResolver.canWrite(eContext, contextObject, name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (AccessException ae) {
|
||||
// let others try
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO when there is more time, remove this and use the version in 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.
|
||||
* @param targetType the type upon which property access is being attempted
|
||||
* @return a list of resolvers that should be tried in order to access the property
|
||||
*/
|
||||
private List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, ExpressionState state) {
|
||||
List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
|
||||
List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
|
||||
for (PropertyAccessor resolver : state.getPropertyAccessors()) {
|
||||
Class<?>[] targets = resolver.getSpecificTargetClasses();
|
||||
if (targets == null) { // generic resolver that says it can be used for any type
|
||||
generalAccessors.add(resolver);
|
||||
}
|
||||
else {
|
||||
if (targetType != null) {
|
||||
for (Class<?> clazz : targets) {
|
||||
if (clazz == targetType) {
|
||||
specificAccessors.add( resolver);
|
||||
break;
|
||||
}
|
||||
else if (clazz.isAssignableFrom(targetType)) {
|
||||
generalAccessors.add(resolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
List<PropertyAccessor> resolvers = new ArrayList<PropertyAccessor>();
|
||||
resolvers.addAll(specificAccessors);
|
||||
generalAccessors.removeAll(specificAccessors);
|
||||
resolvers.addAll(generalAccessors);
|
||||
return resolvers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
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.
|
||||
*
|
||||
* <p>Example: "java.lang.String" as in the expression "new java.lang.String('hello')"
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class QualifiedIdentifier extends SpelNodeImpl {
|
||||
|
||||
// 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();
|
||||
if (i > 0 && !value.toString().startsWith("$")) {
|
||||
sb.append(".");
|
||||
}
|
||||
sb.append(value);
|
||||
}
|
||||
this.value = new TypedValue(sb.toString());
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (this.value != null) {
|
||||
sb.append(this.value.getValue());
|
||||
} else {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(".");
|
||||
}
|
||||
sb.append(getChild(i).toStringAST());
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Represents selection over a map or collection.
|
||||
* For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns [2, 4, 6, 8, 10]
|
||||
*
|
||||
* <p>Basically a subset of the input data is returned based on the
|
||||
* evaluation of the expression supplied as selection criteria.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Selection extends SpelNodeImpl {
|
||||
|
||||
public final static int ALL = 0; // ?[]
|
||||
public final static int FIRST = 1; // ^[]
|
||||
public final static int LAST = 2; // $[]
|
||||
|
||||
private final int variant;
|
||||
private final boolean nullSafe;
|
||||
|
||||
public Selection(boolean nullSafe, int variant,int pos,SpelNodeImpl expression) {
|
||||
super(pos,expression);
|
||||
this.nullSafe = nullSafe;
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
TypedValue op = state.getActiveContextObject();
|
||||
Object operand = op.getValue();
|
||||
|
||||
SpelNodeImpl selectionCriteria = children[0];
|
||||
if (operand instanceof Map) {
|
||||
Map<?, ?> mapdata = (Map<?, ?>) operand;
|
||||
// TODO don't lose generic info for the new map
|
||||
Map<Object,Object> result = new HashMap<Object,Object>();
|
||||
Object lastKey = null;
|
||||
for (Map.Entry entry : mapdata.entrySet()) {
|
||||
try {
|
||||
TypedValue kvpair = new TypedValue(entry);
|
||||
state.pushActiveContextObject(kvpair);
|
||||
Object o = selectionCriteria.getValueInternal(state).getValue();
|
||||
if (o instanceof Boolean) {
|
||||
if (((Boolean) o).booleanValue() == true) {
|
||||
if (variant == FIRST) {
|
||||
result.put(entry.getKey(),entry.getValue());
|
||||
return new TypedValue(result);
|
||||
}
|
||||
result.put(entry.getKey(),entry.getValue());
|
||||
lastKey = entry.getKey();
|
||||
}
|
||||
} else {
|
||||
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
|
||||
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
|
||||
}
|
||||
} finally {
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
|
||||
return new TypedValue(null);
|
||||
}
|
||||
if (variant == LAST) {
|
||||
Map resultMap = new HashMap();
|
||||
Object lastValue = result.get(lastKey);
|
||||
resultMap.put(lastKey,lastValue);
|
||||
return new TypedValue(resultMap);
|
||||
}
|
||||
return new TypedValue(result);
|
||||
} else 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));
|
||||
data.addAll(c);
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
int idx = 0;
|
||||
for (Object element : data) {
|
||||
try {
|
||||
state.pushActiveContextObject(new TypedValue(element));
|
||||
state.enterScope("index", idx);
|
||||
Object o = selectionCriteria.getValueInternal(state).getValue();
|
||||
if (o instanceof Boolean) {
|
||||
if (((Boolean) o).booleanValue() == true) {
|
||||
if (variant == FIRST) {
|
||||
return new TypedValue(element);
|
||||
}
|
||||
result.add(element);
|
||||
}
|
||||
} else {
|
||||
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
|
||||
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
|
||||
}
|
||||
idx++;
|
||||
} finally {
|
||||
state.exitScope();
|
||||
state.popActiveContextObject();
|
||||
}
|
||||
}
|
||||
if ((variant == FIRST || variant == LAST) && result.size() == 0) {
|
||||
return TypedValue.NULL;
|
||||
}
|
||||
if (variant == LAST) {
|
||||
return new TypedValue(result.get(result.size() - 1));
|
||||
}
|
||||
if (operand instanceof Collection) {
|
||||
return new TypedValue(result);
|
||||
}
|
||||
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 TypedValue(resultArray);
|
||||
}
|
||||
} else {
|
||||
if (operand==null) {
|
||||
if (nullSafe) {
|
||||
return TypedValue.NULL;
|
||||
} else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
|
||||
"null");
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
return sb.append(getChild(0).toStringAST()).append("]").toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.common.ExpressionUtils;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
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
|
||||
Assert.isTrue(pos != 0);
|
||||
if (operands != null && operands.length > 0) {
|
||||
this.children = operands;
|
||||
for (SpelNodeImpl childnode : operands) {
|
||||
childnode.parent = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SpelNodeImpl getPreviousChild() {
|
||||
SpelNodeImpl result = null;
|
||||
if (parent != null) {
|
||||
for (SpelNodeImpl child : parent.children) {
|
||||
if (this==child) break;
|
||||
result = child;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public final Object getValue(ExpressionState expressionState) throws EvaluationException {
|
||||
if (expressionState != null) {
|
||||
return getValueInternal(expressionState).getValue();
|
||||
} else {
|
||||
// configuration not set - does that matter?
|
||||
return getValue(new ExpressionState(new StandardEvaluationContext()));
|
||||
}
|
||||
}
|
||||
|
||||
public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
|
||||
if (expressionState != null) {
|
||||
return getValueInternal(expressionState);
|
||||
} else {
|
||||
// configuration not set - does that matter?
|
||||
return getTypedValue(new ExpressionState(new StandardEvaluationContext()));
|
||||
}
|
||||
}
|
||||
|
||||
// by default Ast nodes are not writable
|
||||
public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
|
||||
}
|
||||
|
||||
public SpelNode getChild(int index) {
|
||||
return children[index];
|
||||
}
|
||||
|
||||
public int getChildCount() {
|
||||
return children.length;
|
||||
}
|
||||
|
||||
public Class<?> getObjectClass(Object obj) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
return (obj instanceof Class ? ((Class<?>) obj) : obj.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final <T> T getValue(ExpressionState state, Class<T> desiredReturnType) throws EvaluationException {
|
||||
Object result = getValueInternal(state).getValue();
|
||||
if (result != null && desiredReturnType != null) {
|
||||
Class<?> resultType = result.getClass();
|
||||
if (desiredReturnType.isAssignableFrom(resultType)) {
|
||||
return (T) result;
|
||||
}
|
||||
// Attempt conversion to the requested type, may throw an exception
|
||||
return ExpressionUtils.convert(state.getEvaluationContext(), result, desiredReturnType);
|
||||
}
|
||||
return (T) result;
|
||||
}
|
||||
|
||||
public abstract TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException;
|
||||
|
||||
public abstract String toStringAST();
|
||||
|
||||
public int getStartPosition() {
|
||||
return (pos>>16);
|
||||
}
|
||||
|
||||
public int getEndPosition() {
|
||||
return (pos&0xffff);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
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?
|
||||
value = value.substring(1, value.length() - 1);
|
||||
this.value = new TypedValue(value.replaceAll("''", "'"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue getLiteralValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "'" + getLiteralValue().getValue() + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
|
||||
/**
|
||||
* Represents a ternary expression, for example: "someCheck()?true:false".
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Ternary extends SpelNodeImpl {
|
||||
|
||||
|
||||
public Ternary(int pos, SpelNodeImpl... args) {
|
||||
super(pos,args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the condition and if true evaluate the first alternative, otherwise evaluate the second alternative.
|
||||
* @param state the expression state
|
||||
* @throws EvaluationException if the condition does not evaluate correctly to a boolean or there is a problem
|
||||
* executing the chosen alternative
|
||||
*/
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
Boolean value = 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
return new StringBuilder().append(getChild(0).toStringAST()).append(" ? ").append(getChild(1).toStringAST())
|
||||
.append(" : ").append(getChild(2).toStringAST()).toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
private Class<?> type;
|
||||
|
||||
TypeCode(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public static TypeCode forName(String name) {
|
||||
String searchingFor = name.toUpperCase();
|
||||
TypeCode[] tcs = values();
|
||||
for (int i = 1; i < tcs.length; i++) {
|
||||
if (tcs[i].name().equals(searchingFor)) {
|
||||
return tcs[i];
|
||||
}
|
||||
}
|
||||
return TypeCode.OBJECT;
|
||||
}
|
||||
|
||||
public static TypeCode forClass(Class<?> c) {
|
||||
TypeCode[] allValues = TypeCode.values();
|
||||
for (int i = 0; i < allValues.length; i++) {
|
||||
TypeCode typeCode = allValues[i];
|
||||
if (c == typeCode.getType()) {
|
||||
return typeCode;
|
||||
}
|
||||
}
|
||||
return OBJECT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
|
||||
/**
|
||||
* Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)"
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class TypeReference extends SpelNodeImpl {
|
||||
|
||||
public TypeReference(int pos,SpelNodeImpl qualifiedId) {
|
||||
super(pos,qualifiedId);
|
||||
}
|
||||
|
||||
@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();
|
||||
if (typename.indexOf(".") == -1 && Character.isLowerCase(typename.charAt(0))) {
|
||||
TypeCode tc = TypeCode.valueOf(typename.toUpperCase());
|
||||
if (tc != TypeCode.OBJECT) {
|
||||
// it is a primitive type
|
||||
return new TypedValue(tc.getType());
|
||||
}
|
||||
}
|
||||
return new TypedValue(state.findType(typename));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("T(");
|
||||
sb.append(getChild(0).toStringAST());
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
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
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class VariableReference extends SpelNodeImpl {
|
||||
|
||||
// Well known variables:
|
||||
private final static String THIS = "this"; // currently active context object
|
||||
private final static String ROOT = "root"; // root context object
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
public VariableReference(String variableName, int pos) {
|
||||
super(pos);
|
||||
name = variableName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws SpelEvaluationException {
|
||||
if (this.name.equals(THIS)) {
|
||||
return state.getActiveContextObject();
|
||||
}
|
||||
if (this.name.equals(ROOT)) {
|
||||
return state.getRootContextObject();
|
||||
}
|
||||
TypedValue result = state.lookupVariable(this.name);
|
||||
// a null value will mean either the value was null or the variable was not found
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ExpressionState state, Object value) throws SpelEvaluationException {
|
||||
state.setVariable(this.name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
return "#" + this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWritable(ExpressionState expressionState) throws SpelEvaluationException {
|
||||
return !(this.name.equals(THIS) || this.name.equals(ROOT));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
grammar SpringExpressions;
|
||||
|
||||
options {
|
||||
language = Java;
|
||||
output=AST;
|
||||
k=2;
|
||||
}
|
||||
|
||||
tokens {
|
||||
INTEGER_LITERAL;
|
||||
EXPRESSION;
|
||||
QUALIFIED_IDENTIFIER;
|
||||
PROPERTY_OR_FIELD;
|
||||
INDEXER;
|
||||
CONSTRUCTOR;
|
||||
HOLDER;
|
||||
NAMED_ARGUMENT;
|
||||
FUNCTIONREF;
|
||||
TYPEREF;
|
||||
VARIABLEREF;
|
||||
METHOD;
|
||||
ADD;
|
||||
SUBTRACT;
|
||||
NUMBER;
|
||||
}
|
||||
|
||||
// applies only to the parser:
|
||||
@header {package org.springframework.expression.spel.generated;}
|
||||
|
||||
// applies only to the lexer:
|
||||
@lexer::header {package org.springframework.expression.spel.generated;}
|
||||
|
||||
@members {
|
||||
// For collecting info whilst processing rules that can be used in messages
|
||||
protected Stack<String> paraphrase = new Stack<String>();
|
||||
}
|
||||
|
||||
@rulecatch {
|
||||
catch(RecognitionException e) {
|
||||
reportError(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
expr: expression EOF!;
|
||||
|
||||
expression :
|
||||
logicalOrExpression
|
||||
( (ASSIGN^ logicalOrExpression)
|
||||
| (DEFAULT^ logicalOrExpression)
|
||||
| (QMARK^ expression COLON! expression))?;
|
||||
|
||||
parenExpr : LPAREN! expression RPAREN!;
|
||||
|
||||
logicalOrExpression
|
||||
: logicalAndExpression (OR^ logicalAndExpression)*;
|
||||
|
||||
logicalAndExpression
|
||||
: relationalExpression (AND^ relationalExpression)*;
|
||||
|
||||
relationalExpression : sumExpression (relationalOperator^ sumExpression)?;
|
||||
|
||||
sumExpression
|
||||
: productExpression ( (PLUS^ | MINUS^) productExpression)*;
|
||||
|
||||
productExpression
|
||||
: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ;
|
||||
|
||||
powerExpr : unaryExpression (POWER^ unaryExpression)? ;
|
||||
|
||||
unaryExpression
|
||||
: (PLUS^ | MINUS^ | BANG^) unaryExpression
|
||||
| primaryExpression ;
|
||||
|
||||
primaryExpression
|
||||
: startNode (node)? -> ^(EXPRESSION startNode (node)?);
|
||||
|
||||
startNode
|
||||
:
|
||||
parenExpr
|
||||
| methodOrProperty
|
||||
| functionOrVar
|
||||
| indexer
|
||||
| literal
|
||||
| type
|
||||
| constructor
|
||||
| projection
|
||||
| selection
|
||||
| firstSelection
|
||||
| lastSelection
|
||||
;
|
||||
|
||||
node
|
||||
: ((DOT dottedNode) | nonDottedNode)+;
|
||||
|
||||
nonDottedNode
|
||||
: indexer;
|
||||
|
||||
dottedNode
|
||||
:
|
||||
((methodOrProperty
|
||||
| functionOrVar
|
||||
| projection
|
||||
| selection
|
||||
| firstSelection
|
||||
| lastSelection
|
||||
))
|
||||
;
|
||||
|
||||
functionOrVar
|
||||
: (POUND ID LPAREN) => function
|
||||
| var
|
||||
;
|
||||
|
||||
function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs);
|
||||
|
||||
var : POUND id=ID -> ^(VARIABLEREF[$id]);
|
||||
|
||||
|
||||
methodOrProperty
|
||||
: (ID LPAREN) => id=ID methodArgs -> ^(METHOD[$id] methodArgs)
|
||||
| property
|
||||
;
|
||||
|
||||
// may have to preserve these commas to make it easier to offer suggestions in the right place
|
||||
// mod at 9th feb 19:13 - added the second 'COMMA?' to allow for code completion "foo(A,"
|
||||
// TODO need to preserve commas and then check for badly formed call later (optimizing tree walk) to disallow "foo(a,b,c,)"
|
||||
methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!;
|
||||
|
||||
// If we match ID then create a node called PROPERTY_OR_FIELD and copy the id info into it.
|
||||
// this means the propertyOrField.text is what id.text would have been, rather than having to
|
||||
// access id as a child of the new node.
|
||||
property: id=ID -> ^(PROPERTY_OR_FIELD[$id]);
|
||||
|
||||
|
||||
indexer: LBRACKET r1=argument (COMMA r2=argument)* RBRACKET -> ^(INDEXER $r1 ($r2)*);
|
||||
|
||||
// argument;
|
||||
// TODO make expression conditional with ? if want completion for when the RCURLY is missing
|
||||
projection: PROJECT^ expression RBRACKET!;
|
||||
|
||||
selection: SELECT^ expression RBRACKET!;
|
||||
|
||||
firstSelection: SELECT_FIRST^ expression RBRACKET!;
|
||||
|
||||
lastSelection: SELECT_LAST^ expression RBRACKET!;
|
||||
|
||||
// TODO cope with array types
|
||||
type: TYPE qualifiedId RPAREN -> ^(TYPEREF qualifiedId);
|
||||
//type: TYPE tn=qualifiedId (LBRACKET RBRACKET)? (COMMA qid=qualifiedId)? RPAREN
|
||||
|
||||
|
||||
constructor
|
||||
: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs)
|
||||
;
|
||||
|
||||
ctorArgs
|
||||
: LPAREN! (namedArgument (COMMA! namedArgument)*)? RPAREN!;
|
||||
|
||||
argument : expression;
|
||||
|
||||
namedArgument
|
||||
: (ID ASSIGN) => id=ID ASSIGN expression
|
||||
-> ^(NAMED_ARGUMENT[$id] expression)
|
||||
| argument ;
|
||||
|
||||
qualifiedId : ID (DOT ID)* -> ^(QUALIFIED_IDENTIFIER ID*);
|
||||
|
||||
contextName : ID (DIV ID)* -> ^(QUALIFIED_IDENTIFIER ID*);
|
||||
|
||||
literal
|
||||
: INTEGER_LITERAL
|
||||
| STRING_LITERAL
|
||||
| DQ_STRING_LITERAL
|
||||
| boolLiteral
|
||||
| NULL_LITERAL
|
||||
| HEXADECIMAL_INTEGER_LITERAL
|
||||
| REAL_LITERAL
|
||||
;
|
||||
|
||||
boolLiteral: TRUE | FALSE;
|
||||
|
||||
INTEGER_LITERAL
|
||||
: (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
|
||||
|
||||
HEXADECIMAL_INTEGER_LITERAL : ('0x' | '0X') (HEX_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
|
||||
|
||||
relationalOperator
|
||||
: EQUAL
|
||||
| NOT_EQUAL
|
||||
| LESS_THAN
|
||||
| LESS_THAN_OR_EQUAL
|
||||
| GREATER_THAN
|
||||
| GREATER_THAN_OR_EQUAL
|
||||
| INSTANCEOF
|
||||
| BETWEEN
|
||||
| MATCHES
|
||||
;
|
||||
|
||||
ASSIGN: '=';
|
||||
EQUAL: '==';
|
||||
NOT_EQUAL: '!=';
|
||||
LESS_THAN: '<';
|
||||
LESS_THAN_OR_EQUAL: '<=';
|
||||
GREATER_THAN: '>';
|
||||
GREATER_THAN_OR_EQUAL: '>=';
|
||||
INSTANCEOF: 'instanceof';
|
||||
BETWEEN:'between';
|
||||
MATCHES:'matches';
|
||||
NULL_LITERAL: 'null';
|
||||
|
||||
SEMI: ';';
|
||||
DOT: '.';
|
||||
COMMA: ',';
|
||||
LPAREN: '(';
|
||||
RPAREN: ')';
|
||||
LCURLY: '{';
|
||||
RCURLY: '}';
|
||||
LBRACKET: '[';
|
||||
RBRACKET: ']';
|
||||
PIPE: '|';
|
||||
|
||||
AND: 'and';
|
||||
OR: 'or';
|
||||
FALSE: 'false';
|
||||
TRUE: 'true';
|
||||
|
||||
PLUS: '+';
|
||||
MINUS: '-';
|
||||
DIV: '/';
|
||||
STAR: '*';
|
||||
MOD: '%';
|
||||
POWER: '^';
|
||||
BANG: '!';
|
||||
POUND: '#';
|
||||
QMARK: '?';
|
||||
DEFAULT: '??';
|
||||
PROJECT: '![';
|
||||
SELECT: '?[';
|
||||
SELECT_FIRST: '^[';
|
||||
SELECT_LAST: '$[';
|
||||
TYPE: 'T(';
|
||||
|
||||
STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
|
||||
DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
|
||||
ID: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*;
|
||||
DOT_ESCAPED: '\\.';
|
||||
WS: ( ' ' | '\t' | '\n' |'\r')+ { $channel=HIDDEN; } ;
|
||||
DOLLAR: '$';
|
||||
AT: '@';
|
||||
UPTO: '..';
|
||||
COLON: ':';
|
||||
|
||||
|
||||
REAL_LITERAL :
|
||||
('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
|
||||
((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
|
||||
((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
|
||||
((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
|
||||
|
||||
fragment APOS : '\''! '\'';
|
||||
fragment DECIMAL_DIGIT : '0'..'9' ;
|
||||
fragment INTEGER_TYPE_SUFFIX : ( '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';
|
||||
@@ -1,850 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.standard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.TemplateAwareExpressionParser;
|
||||
import org.springframework.expression.spel.InternalParseException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelParseException;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.ast.Assign;
|
||||
import org.springframework.expression.spel.ast.BeanReference;
|
||||
import org.springframework.expression.spel.ast.BooleanLiteral;
|
||||
import org.springframework.expression.spel.ast.CompoundExpression;
|
||||
import org.springframework.expression.spel.ast.ConstructorReference;
|
||||
import org.springframework.expression.spel.ast.Elvis;
|
||||
import org.springframework.expression.spel.ast.FunctionReference;
|
||||
import org.springframework.expression.spel.ast.Identifier;
|
||||
import org.springframework.expression.spel.ast.Indexer;
|
||||
import org.springframework.expression.spel.ast.InlineList;
|
||||
import org.springframework.expression.spel.ast.Literal;
|
||||
import org.springframework.expression.spel.ast.MethodReference;
|
||||
import org.springframework.expression.spel.ast.NullLiteral;
|
||||
import org.springframework.expression.spel.ast.OpAnd;
|
||||
import org.springframework.expression.spel.ast.OpDivide;
|
||||
import org.springframework.expression.spel.ast.OpEQ;
|
||||
import org.springframework.expression.spel.ast.OpGE;
|
||||
import org.springframework.expression.spel.ast.OpGT;
|
||||
import org.springframework.expression.spel.ast.OpLE;
|
||||
import org.springframework.expression.spel.ast.OpLT;
|
||||
import org.springframework.expression.spel.ast.OpMinus;
|
||||
import org.springframework.expression.spel.ast.OpModulus;
|
||||
import org.springframework.expression.spel.ast.OpMultiply;
|
||||
import org.springframework.expression.spel.ast.OpNE;
|
||||
import org.springframework.expression.spel.ast.OpOr;
|
||||
import org.springframework.expression.spel.ast.OpPlus;
|
||||
import org.springframework.expression.spel.ast.OperatorInstanceof;
|
||||
import org.springframework.expression.spel.ast.OperatorMatches;
|
||||
import org.springframework.expression.spel.ast.OperatorNot;
|
||||
import org.springframework.expression.spel.ast.OperatorPower;
|
||||
import org.springframework.expression.spel.ast.Projection;
|
||||
import org.springframework.expression.spel.ast.PropertyOrFieldReference;
|
||||
import org.springframework.expression.spel.ast.QualifiedIdentifier;
|
||||
import org.springframework.expression.spel.ast.Selection;
|
||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
||||
import org.springframework.expression.spel.ast.StringLiteral;
|
||||
import org.springframework.expression.spel.ast.Ternary;
|
||||
import org.springframework.expression.spel.ast.TypeReference;
|
||||
import org.springframework.expression.spel.ast.VariableReference;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Hand written SpEL parser. Instances are reusable but are not thread safe.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
|
||||
// The expression being parsed
|
||||
private String expressionString;
|
||||
|
||||
// The token stream constructed from that expression string
|
||||
private List<Token> tokenStream;
|
||||
|
||||
// length of a populated token stream
|
||||
private int tokenStreamLength;
|
||||
|
||||
// Current location in the token stream when processing tokens
|
||||
private int tokenStreamPointer;
|
||||
|
||||
// For rules that build nodes, they are stacked here for return
|
||||
private Stack<SpelNodeImpl> constructedNodes = new Stack<SpelNodeImpl>();
|
||||
|
||||
private SpelParserConfiguration configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Create a parser with some configured behavior.
|
||||
* @param configuration custom configuration options
|
||||
*/
|
||||
public InternalSpelExpressionParser(SpelParserConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
|
||||
try {
|
||||
this.expressionString = expressionString;
|
||||
Tokenizer tokenizer = new Tokenizer(expressionString);
|
||||
tokenizer.process();
|
||||
tokenStream = tokenizer.getTokens();
|
||||
tokenStreamLength = tokenStream.size();
|
||||
tokenStreamPointer = 0;
|
||||
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);
|
||||
}
|
||||
catch (InternalParseException ipe) {
|
||||
throw ipe.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
// expression
|
||||
// : logicalOrExpression
|
||||
// ( (ASSIGN^ logicalOrExpression)
|
||||
// | (DEFAULT^ logicalOrExpression)
|
||||
// | (QMARK^ expression COLON! expression)
|
||||
// | (ELVIS^ expression))?;
|
||||
private SpelNodeImpl eatExpression() {
|
||||
SpelNodeImpl expr = eatLogicalOrExpression();
|
||||
if (moreTokens()) {
|
||||
Token t = peekToken();
|
||||
if (t.kind==TokenKind.ASSIGN) { // a=b
|
||||
if (expr==null) {
|
||||
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1));
|
||||
}
|
||||
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 (expr==null) {
|
||||
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-2));
|
||||
}
|
||||
nextToken(); // elvis has left the building
|
||||
SpelNodeImpl valueIfNull = eatExpression();
|
||||
if (valueIfNull==null) {
|
||||
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 (expr==null) {
|
||||
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1));
|
||||
}
|
||||
nextToken();
|
||||
SpelNodeImpl ifTrueExprValue = eatExpression();
|
||||
eatToken(TokenKind.COLON);
|
||||
SpelNodeImpl ifFalseExprValue = eatExpression();
|
||||
return new Ternary(toPos(t),expr,ifTrueExprValue,ifFalseExprValue);
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
//logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*;
|
||||
private SpelNodeImpl eatLogicalOrExpression() {
|
||||
SpelNodeImpl expr = eatLogicalAndExpression();
|
||||
while (peekIdentifierToken("or")) {
|
||||
Token t = nextToken(); //consume OR
|
||||
SpelNodeImpl rhExpr = eatLogicalAndExpression();
|
||||
checkRightOperand(t,rhExpr);
|
||||
expr = new OpOr(toPos(t),expr,rhExpr);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
// logicalAndExpression : relationalExpression (AND^ relationalExpression)*;
|
||||
private SpelNodeImpl eatLogicalAndExpression() {
|
||||
SpelNodeImpl expr = eatRelationalExpression();
|
||||
while (peekIdentifierToken("and")) {
|
||||
Token t = nextToken();// consume 'AND'
|
||||
SpelNodeImpl rhExpr = eatRelationalExpression();
|
||||
checkRightOperand(t,rhExpr);
|
||||
expr = new OpAnd(toPos(t),expr,rhExpr);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
// relationalExpression : sumExpression (relationalOperator^ sumExpression)?;
|
||||
private SpelNodeImpl eatRelationalExpression() {
|
||||
SpelNodeImpl expr = eatSumExpression();
|
||||
Token relationalOperatorToken = maybeEatRelationalOperator();
|
||||
if (relationalOperatorToken != null) {
|
||||
Token t = nextToken(); //consume relational operator token
|
||||
SpelNodeImpl rhExpr = eatSumExpression();
|
||||
checkRightOperand(t,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.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);
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
//sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*;
|
||||
private SpelNodeImpl eatSumExpression() {
|
||||
SpelNodeImpl expr = eatProductExpression();
|
||||
while (peekToken(TokenKind.PLUS,TokenKind.MINUS)) {
|
||||
Token t = nextToken();//consume PLUS or MINUS
|
||||
SpelNodeImpl rhExpr = eatProductExpression();
|
||||
checkRightOperand(t,rhExpr);
|
||||
if (t.kind==TokenKind.PLUS) {
|
||||
expr = new OpPlus(toPos(t),expr,rhExpr);
|
||||
} else {
|
||||
Assert.isTrue(t.kind==TokenKind.MINUS);
|
||||
expr = new OpMinus(toPos(t),expr,rhExpr);
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
// productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ;
|
||||
private SpelNodeImpl eatProductExpression() {
|
||||
SpelNodeImpl expr = eatPowerExpression();
|
||||
while (peekToken(TokenKind.STAR,TokenKind.DIV,TokenKind.MOD)) {
|
||||
Token t = nextToken(); // consume STAR/DIV/MOD
|
||||
SpelNodeImpl rhExpr = eatPowerExpression();
|
||||
checkRightOperand(t,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;
|
||||
}
|
||||
|
||||
// powerExpr : unaryExpression (POWER^ unaryExpression)? ;
|
||||
private SpelNodeImpl eatPowerExpression() {
|
||||
SpelNodeImpl expr = eatUnaryExpression();
|
||||
if (peekToken(TokenKind.POWER)) {
|
||||
Token t = nextToken();//consume POWER
|
||||
SpelNodeImpl rhExpr = eatUnaryExpression();
|
||||
checkRightOperand(t,rhExpr);
|
||||
return new OperatorPower(toPos(t),expr, rhExpr);
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
// unaryExpression: (PLUS^ | MINUS^ | BANG^) unaryExpression | primaryExpression ;
|
||||
private SpelNodeImpl eatUnaryExpression() {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
return eatPrimaryExpression();
|
||||
}
|
||||
}
|
||||
|
||||
// primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?);
|
||||
private SpelNodeImpl eatPrimaryExpression() {
|
||||
List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>();
|
||||
SpelNodeImpl start = eatStartNode(); // always a start node
|
||||
nodes.add(start);
|
||||
while (maybeEatNode()) {
|
||||
nodes.add(pop());
|
||||
}
|
||||
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()]));
|
||||
}
|
||||
}
|
||||
|
||||
// node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+;
|
||||
private boolean maybeEatNode() {
|
||||
SpelNodeImpl expr = null;
|
||||
if (peekToken(TokenKind.DOT,TokenKind.SAFE_NAVI)) {
|
||||
expr = eatDottedNode();
|
||||
} else {
|
||||
expr = maybeEatNonDottedNode();
|
||||
}
|
||||
if (expr==null) {
|
||||
return false;
|
||||
} else {
|
||||
push(expr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// nonDottedNode: indexer;
|
||||
private SpelNodeImpl maybeEatNonDottedNode() {
|
||||
if (peekToken(TokenKind.LSQUARE)) {
|
||||
if (maybeEatIndexer()) {
|
||||
return pop();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//dottedNode
|
||||
// : ((methodOrProperty
|
||||
// | functionOrVar
|
||||
// | projection
|
||||
// | selection
|
||||
// | firstSelection
|
||||
// | lastSelection
|
||||
// ))
|
||||
// ;
|
||||
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)) {
|
||||
return pop();
|
||||
}
|
||||
if (peekToken()==null) {
|
||||
// unexpectedly ran out of data
|
||||
raiseInternalException(t.startpos,SpelMessage.OOD);
|
||||
} else {
|
||||
raiseInternalException(t.startpos,SpelMessage.UNEXPECTED_DATA_AFTER_DOT,toString(peekToken()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// functionOrVar
|
||||
// : (POUND ID LPAREN) => function
|
||||
// | var
|
||||
//
|
||||
// function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs);
|
||||
// var : POUND id=ID -> ^(VARIABLEREF[$id]);
|
||||
private boolean maybeEatFunctionOrVar() {
|
||||
if (!peekToken(TokenKind.HASH)) {
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!;
|
||||
private SpelNodeImpl[] maybeEatMethodArgs() {
|
||||
if (!peekToken(TokenKind.LPAREN)) {
|
||||
return null;
|
||||
}
|
||||
List<SpelNodeImpl> args = new ArrayList<SpelNodeImpl>();
|
||||
consumeArguments(args);
|
||||
eatToken(TokenKind.RPAREN);
|
||||
return args.toArray(new SpelNodeImpl[args.size()]);
|
||||
}
|
||||
|
||||
private void eatConstructorArgs(List<SpelNodeImpl> accumulatedArguments) {
|
||||
if (!peekToken(TokenKind.LPAREN)) {
|
||||
throw new InternalParseException(new SpelParseException(expressionString,positionOf(peekToken()),SpelMessage.MISSING_CONSTRUCTOR_ARGS));
|
||||
}
|
||||
consumeArguments(accumulatedArguments);
|
||||
eatToken(TokenKind.RPAREN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for consuming arguments for either a method or a constructor call
|
||||
*/
|
||||
private void consumeArguments(List<SpelNodeImpl> accumulatedArguments) {
|
||||
int pos = peekToken().startpos;
|
||||
Token next = null;
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
|
||||
private int positionOf(Token t) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//startNode
|
||||
// : parenExpr | literal
|
||||
// | type
|
||||
// | methodOrProperty
|
||||
// | functionOrVar
|
||||
// | projection
|
||||
// | selection
|
||||
// | firstSelection
|
||||
// | lastSelection
|
||||
// | indexer
|
||||
// | constructor
|
||||
private SpelNodeImpl eatStartNode() {
|
||||
if (maybeEatLiteral()) {
|
||||
return pop();
|
||||
} else if (maybeEatParenExpression()) {
|
||||
return pop();
|
||||
} else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) {
|
||||
return pop();
|
||||
} else if (maybeEatBeanReference()) {
|
||||
return pop();
|
||||
} else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
|
||||
return pop();
|
||||
} else if (maybeEatInlineList()) {
|
||||
return pop();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// parse: @beanname @'bean.name'
|
||||
// quoted if dotted
|
||||
private boolean maybeEatBeanReference() {
|
||||
if (peekToken(TokenKind.BEAN_REF)) {
|
||||
Token beanRefToken = nextToken();
|
||||
Token beanNameToken = null;
|
||||
String beanname = null;
|
||||
if (peekToken(TokenKind.IDENTIFIER)) {
|
||||
beanNameToken = eatToken(TokenKind.IDENTIFIER);
|
||||
beanname = beanNameToken.data;
|
||||
} 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);
|
||||
}
|
||||
|
||||
BeanReference beanReference = new BeanReference(toPos(beanNameToken),beanname);
|
||||
constructedNodes.push(beanReference);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean maybeEatTypeReference() {
|
||||
if (peekToken(TokenKind.IDENTIFIER)) {
|
||||
Token typeName = peekToken();
|
||||
if (!typeName.stringValue().equals("T")) {
|
||||
return false;
|
||||
}
|
||||
nextToken();
|
||||
eatToken(TokenKind.LPAREN);
|
||||
SpelNodeImpl node = eatPossiblyQualifiedId();
|
||||
// dotted qualified id
|
||||
eatToken(TokenKind.RPAREN);
|
||||
constructedNodes.push(new TypeReference(toPos(typeName),node));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean maybeEatNullReference() {
|
||||
if (peekToken(TokenKind.IDENTIFIER)) {
|
||||
Token nullToken = peekToken();
|
||||
if (!nullToken.stringValue().equals("null")) {
|
||||
return false;
|
||||
}
|
||||
nextToken();
|
||||
constructedNodes.push(new NullLiteral(toPos(nullToken)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//projection: PROJECT^ expression RCURLY!;
|
||||
private boolean maybeEatProjection(boolean nullSafeNavigation) {
|
||||
Token t = peekToken();
|
||||
if (!peekToken(TokenKind.PROJECT,true)) {
|
||||
return false;
|
||||
}
|
||||
SpelNodeImpl expr = eatExpression();
|
||||
eatToken(TokenKind.RSQUARE);
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
SpelNodeImpl expr = null;
|
||||
Token closingCurly = peekToken();
|
||||
if (peekToken(TokenKind.RCURLY,true)) {
|
||||
// empty list '[]'
|
||||
expr = new InlineList(toPos(t.startpos,closingCurly.endpos));
|
||||
} 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);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean maybeEatIndexer() {
|
||||
Token t = peekToken();
|
||||
if (!peekToken(TokenKind.LSQUARE,true)) {
|
||||
return false;
|
||||
}
|
||||
SpelNodeImpl expr = eatExpression();
|
||||
eatToken(TokenKind.RSQUARE);
|
||||
constructedNodes.push(new Indexer(toPos(t),expr));
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean maybeEatSelection(boolean nullSafeNavigation) {
|
||||
Token t = peekToken();
|
||||
if (!peekSelectToken()) {
|
||||
return false;
|
||||
}
|
||||
nextToken();
|
||||
SpelNodeImpl expr = eatExpression();
|
||||
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));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eat an identifier, possibly qualified (meaning that it is dotted).
|
||||
* TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c)
|
||||
*/
|
||||
private SpelNodeImpl eatPossiblyQualifiedId() {
|
||||
List<SpelNodeImpl> qualifiedIdPieces = new ArrayList<SpelNodeImpl>();
|
||||
Token startnode = eatToken(TokenKind.IDENTIFIER);
|
||||
qualifiedIdPieces.add(new Identifier(startnode.stringValue(),toPos(startnode)));
|
||||
while (peekToken(TokenKind.DOT,true)) {
|
||||
Token node = eatToken(TokenKind.IDENTIFIER);
|
||||
qualifiedIdPieces.add(new Identifier(node.stringValue(),toPos(node)));
|
||||
}
|
||||
return new QualifiedIdentifier(toPos(startnode.startpos,qualifiedIdPieces.get(qualifiedIdPieces.size()-1).getEndPosition()),qualifiedIdPieces.toArray(new SpelNodeImpl[qualifiedIdPieces.size()]));
|
||||
}
|
||||
|
||||
// This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but
|
||||
// there we want to combine a series of identifiers and dollars into a single identifier
|
||||
private boolean maybeEatMethodOrProperty(boolean nullSafeNavigation) {
|
||||
if (peekToken(TokenKind.IDENTIFIER)) {
|
||||
Token methodOrPropertyName = nextToken();
|
||||
SpelNodeImpl[] args = maybeEatMethodArgs();
|
||||
if (args==null) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
//constructor
|
||||
//: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs)
|
||||
private boolean maybeEatConstructorReference() {
|
||||
if (peekIdentifierToken("new")) {
|
||||
Token newToken = nextToken();
|
||||
SpelNodeImpl possiblyQualifiedConstructorName = eatPossiblyQualifiedId();
|
||||
List<SpelNodeImpl> nodes = new ArrayList<SpelNodeImpl>();
|
||||
nodes.add(possiblyQualifiedConstructorName);
|
||||
if (peekToken(TokenKind.LSQUARE)) {
|
||||
// array initializer
|
||||
List<SpelNodeImpl> dimensions = new ArrayList<SpelNodeImpl>();
|
||||
while (peekToken(TokenKind.LSQUARE,true)) {
|
||||
if (!peekToken(TokenKind.RSQUARE)) {
|
||||
dimensions.add(eatExpression());
|
||||
} else {
|
||||
dimensions.add(null);
|
||||
}
|
||||
eatToken(TokenKind.RSQUARE);
|
||||
}
|
||||
if (maybeEatInlineList()) {
|
||||
nodes.add(pop());
|
||||
}
|
||||
push(new ConstructorReference(toPos(newToken), dimensions.toArray(new SpelNodeImpl[dimensions.size()]),
|
||||
nodes.toArray(new SpelNodeImpl[nodes.size()])));
|
||||
} else {
|
||||
// regular constructor invocation
|
||||
eatConstructorArgs(nodes);
|
||||
// TODO correct end position?
|
||||
push(new ConstructorReference(toPos(newToken), nodes.toArray(new SpelNodeImpl[nodes.size()])));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void push(SpelNodeImpl newNode) {
|
||||
constructedNodes.push(newNode);
|
||||
}
|
||||
|
||||
private SpelNodeImpl pop() {
|
||||
return constructedNodes.pop();
|
||||
}
|
||||
|
||||
// literal
|
||||
// : INTEGER_LITERAL
|
||||
// | boolLiteral
|
||||
// | STRING_LITERAL
|
||||
// | HEXADECIMAL_INTEGER_LITERAL
|
||||
// | REAL_LITERAL
|
||||
// | DQ_STRING_LITERAL
|
||||
// | NULL_LITERAL
|
||||
private boolean maybeEatLiteral() {
|
||||
Token t = peekToken();
|
||||
if (t==null) {
|
||||
return false;
|
||||
}
|
||||
if (t.kind==TokenKind.LITERAL_INT) {
|
||||
push(Literal.getIntLiteral(t.data, toPos(t), 10));
|
||||
} else if (t.kind==TokenKind.LITERAL_LONG) {
|
||||
push(Literal.getLongLiteral(t.data, toPos(t), 10));
|
||||
} else if (t.kind==TokenKind.LITERAL_HEXINT) {
|
||||
push(Literal.getIntLiteral(t.data, toPos(t), 16));
|
||||
} 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) {
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
nextToken();
|
||||
return true;
|
||||
}
|
||||
|
||||
//parenExpr : LPAREN! expression RPAREN!;
|
||||
private boolean maybeEatParenExpression() {
|
||||
if (peekToken(TokenKind.LPAREN)) {
|
||||
nextToken();
|
||||
SpelNodeImpl expr = eatExpression();
|
||||
eatToken(TokenKind.RPAREN);
|
||||
push(expr);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// relationalOperator
|
||||
// : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN
|
||||
// | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES
|
||||
private Token maybeEatRelationalOperator() {
|
||||
Token t = peekToken();
|
||||
if (t==null) {
|
||||
return null;
|
||||
}
|
||||
if (t.isNumericRelationalOperator()) {
|
||||
return t;
|
||||
}
|
||||
if (t.isIdentifier()) {
|
||||
String idString = t.stringValue();
|
||||
if (idString.equalsIgnoreCase("instanceof")) {
|
||||
return t.asInstanceOfToken();
|
||||
} else if (idString.equalsIgnoreCase("matches")) {
|
||||
return t.asMatchesToken();
|
||||
} else if (idString.equalsIgnoreCase("between")) {
|
||||
return t.asBetweenToken();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Token eatToken(TokenKind expectedKind) {
|
||||
Token t = nextToken();
|
||||
if (t==null) {
|
||||
raiseInternalException( expressionString.length(), SpelMessage.OOD);
|
||||
}
|
||||
if (t.kind!=expectedKind) {
|
||||
raiseInternalException(t.startpos,SpelMessage.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),t.getKind().toString().toLowerCase());
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
private boolean peekToken(TokenKind desiredTokenKind) {
|
||||
return peekToken(desiredTokenKind,false);
|
||||
}
|
||||
|
||||
private boolean peekToken(TokenKind desiredTokenKind, boolean consumeIfMatched) {
|
||||
if (!moreTokens()) {
|
||||
return false;
|
||||
}
|
||||
Token t = peekToken();
|
||||
if (t.kind==desiredTokenKind) {
|
||||
if (consumeIfMatched) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean peekToken(TokenKind possible1,TokenKind possible2) {
|
||||
if (!moreTokens()) return false;
|
||||
Token t = peekToken();
|
||||
return t.kind==possible1 || t.kind==possible2;
|
||||
}
|
||||
|
||||
private boolean peekToken(TokenKind possible1,TokenKind possible2, TokenKind possible3) {
|
||||
if (!moreTokens()) return false;
|
||||
Token t = peekToken();
|
||||
return t.kind==possible1 || t.kind==possible2 || t.kind==possible3;
|
||||
}
|
||||
|
||||
private boolean peekIdentifierToken(String identifierString) {
|
||||
if (!moreTokens()) {
|
||||
return false;
|
||||
}
|
||||
Token t = peekToken();
|
||||
return t.kind==TokenKind.IDENTIFIER && t.stringValue().equalsIgnoreCase(identifierString);
|
||||
}
|
||||
|
||||
private boolean peekSelectToken() {
|
||||
if (!moreTokens()) return false;
|
||||
Token t = peekToken();
|
||||
return t.kind==TokenKind.SELECT || t.kind==TokenKind.SELECT_FIRST || t.kind==TokenKind.SELECT_LAST;
|
||||
}
|
||||
|
||||
|
||||
private boolean moreTokens() {
|
||||
return tokenStreamPointer<tokenStream.size();
|
||||
}
|
||||
|
||||
private Token nextToken() {
|
||||
if (tokenStreamPointer>=tokenStreamLength) {
|
||||
return null;
|
||||
}
|
||||
return tokenStream.get(tokenStreamPointer++);
|
||||
}
|
||||
|
||||
private Token peekToken() {
|
||||
if (tokenStreamPointer>=tokenStreamLength) {
|
||||
return null;
|
||||
}
|
||||
return tokenStream.get(tokenStreamPointer);
|
||||
}
|
||||
|
||||
private void raiseInternalException(int pos, SpelMessage message,Object... inserts) {
|
||||
throw new InternalParseException(new SpelParseException(expressionString,pos,message,inserts));
|
||||
}
|
||||
|
||||
public String toString(Token t) {
|
||||
if (t.getKind().hasPayload()) {
|
||||
return t.stringValue();
|
||||
} else {
|
||||
return t.kind.toString().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRightOperand(Token token, SpelNodeImpl operandExpression) {
|
||||
if (operandExpression==null) {
|
||||
raiseInternalException(token.startpos,SpelMessage.RIGHT_OPERAND_PROBLEM);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress the start and end of a token into a single int
|
||||
*/
|
||||
private int toPos(Token t) {
|
||||
return (t.startpos<<16)+t.endpos;
|
||||
}
|
||||
|
||||
private int toPos(int start,int end) {
|
||||
return (start<<16)+end;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.standard;
|
||||
|
||||
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;
|
||||
import org.springframework.expression.common.ExpressionUtils;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelNode;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.expression.spel.ast.SpelNodeImpl;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A SpelExpressions represents a parsed (valid) expression that is ready to be evaluated in a specified context. An
|
||||
* expression can be evaluated standalone or in a specified context. During expression evaluation the context may be
|
||||
* asked to resolve references to types, beans, properties, methods.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpelExpression implements Expression {
|
||||
|
||||
private final String expression;
|
||||
|
||||
private final SpelNodeImpl ast;
|
||||
|
||||
private final SpelParserConfiguration configuration;
|
||||
|
||||
// the default context is used if no override is supplied by the user
|
||||
private EvaluationContext defaultContext;
|
||||
|
||||
|
||||
/**
|
||||
* Construct an expression, only used by the parser.
|
||||
*/
|
||||
public SpelExpression(String expression, SpelNodeImpl ast, SpelParserConfiguration configuration) {
|
||||
this.expression = expression;
|
||||
this.ast = ast;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
// implementing Expression
|
||||
|
||||
public Object getValue() throws EvaluationException {
|
||||
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), configuration);
|
||||
return ast.getValue(expressionState);
|
||||
}
|
||||
|
||||
public Object getValue(Object rootObject) throws EvaluationException {
|
||||
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
|
||||
return ast.getValue(expressionState);
|
||||
}
|
||||
|
||||
public <T> T getValue(Class<T> expectedResultType) throws EvaluationException {
|
||||
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), configuration);
|
||||
TypedValue typedResultValue = ast.getTypedValue(expressionState);
|
||||
return ExpressionUtils.convertTypedValue(expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
|
||||
}
|
||||
|
||||
public <T> T getValue(Object rootObject, Class<T> expectedResultType) throws EvaluationException {
|
||||
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
|
||||
TypedValue typedResultValue = ast.getTypedValue(expressionState);
|
||||
return ExpressionUtils.convertTypedValue(expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
|
||||
}
|
||||
|
||||
public Object getValue(EvaluationContext context) throws EvaluationException {
|
||||
Assert.notNull(context, "The EvaluationContext is required");
|
||||
return ast.getValue(new ExpressionState(context, configuration));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public <T> T getValue(EvaluationContext context, Class<T> expectedResultType) throws EvaluationException {
|
||||
TypedValue typedResultValue = ast.getTypedValue(new ExpressionState(context, configuration));
|
||||
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
|
||||
}
|
||||
|
||||
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> expectedResultType) throws EvaluationException {
|
||||
TypedValue typedResultValue = ast.getTypedValue(new ExpressionState(context, toTypedValue(rootObject), configuration));
|
||||
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
|
||||
}
|
||||
|
||||
public Class getValueType() throws EvaluationException {
|
||||
return getValueType(getEvaluationContext());
|
||||
}
|
||||
|
||||
public Class getValueType(Object rootObject) throws EvaluationException {
|
||||
return getValueType(getEvaluationContext(), rootObject);
|
||||
}
|
||||
|
||||
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();
|
||||
return typeDescriptor != null ? typeDescriptor.getType() : null;
|
||||
}
|
||||
|
||||
public Class getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
|
||||
ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration);
|
||||
TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
|
||||
return typeDescriptor != null ? typeDescriptor.getType() : null;
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException {
|
||||
return getValueTypeDescriptor(getEvaluationContext());
|
||||
}
|
||||
|
||||
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
|
||||
ExpressionState eState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
|
||||
return ast.getValueInternal(eState).getTypeDescriptor();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public String getExpressionString() {
|
||||
return expression;
|
||||
}
|
||||
|
||||
public boolean isWritable(EvaluationContext context) throws EvaluationException {
|
||||
Assert.notNull(context, "The EvaluationContext is required");
|
||||
return ast.isWritable(new ExpressionState(context, configuration));
|
||||
}
|
||||
|
||||
public boolean isWritable(Object rootObject) throws EvaluationException {
|
||||
return ast.isWritable(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
|
||||
Assert.notNull(context, "The EvaluationContext is required");
|
||||
ast.setValue(new ExpressionState(context, configuration), value);
|
||||
}
|
||||
|
||||
public void setValue(Object rootObject, Object value) throws EvaluationException {
|
||||
ast.setValue(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration), value);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// impl only
|
||||
|
||||
/**
|
||||
* @return return the Abstract Syntax Tree for the expression
|
||||
*/
|
||||
public SpelNode getAST() {
|
||||
return ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a string representation of the Abstract Syntax Tree for the expression, this should ideally look like the
|
||||
* input expression, but properly formatted since any unnecessary whitespace will have been discarded during the
|
||||
* parse of the expression.
|
||||
* @return the string representation of the AST
|
||||
*/
|
||||
public String toStringAST() {
|
||||
return ast.toStringAST();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default evaluation context that will be used if none is supplied on an evaluation call
|
||||
* @return the default evaluation context
|
||||
*/
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
if (defaultContext == null) {
|
||||
defaultContext = new StandardEvaluationContext();
|
||||
}
|
||||
return defaultContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the evaluation context that will be used if none is specified on an evaluation call.
|
||||
* @param context an evaluation context
|
||||
*/
|
||||
public void setEvaluationContext(EvaluationContext context) {
|
||||
this.defaultContext = context;
|
||||
}
|
||||
|
||||
private TypedValue toTypedValue(Object object) {
|
||||
if (object == null) {
|
||||
return TypedValue.NULL;
|
||||
}
|
||||
else {
|
||||
return new TypedValue(object);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.standard;
|
||||
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.TemplateAwareExpressionParser;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* SpEL parser. Instances are reusable and thread-safe.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
|
||||
private final SpelParserConfiguration configuration;
|
||||
|
||||
|
||||
/**
|
||||
* Create a parser with standard configuration.
|
||||
*/
|
||||
public SpelExpressionParser() {
|
||||
this.configuration = new SpelParserConfiguration(false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a parser with some configured behavior.
|
||||
* @param configuration custom configuration options
|
||||
*/
|
||||
public SpelExpressionParser(SpelParserConfiguration configuration) {
|
||||
Assert.notNull(configuration, "SpelParserConfiguration must not be null");
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
|
||||
return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
|
||||
}
|
||||
|
||||
public SpelExpression parseRaw(String expressionString) throws ParseException {
|
||||
return doParseExpression(expressionString, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.standard;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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 '+')
|
||||
* @param startpos the exact start
|
||||
* @param endpos the index to the last character
|
||||
*/
|
||||
Token(TokenKind tokenKind, int startpos, int endpos) {
|
||||
this.kind = tokenKind;
|
||||
this.startpos = startpos;
|
||||
this.endpos = endpos;
|
||||
}
|
||||
|
||||
Token(TokenKind tokenKind, char[] tokenData, int pos, int endpos) {
|
||||
this(tokenKind,pos,endpos);
|
||||
this.data = new String(tokenData);
|
||||
}
|
||||
|
||||
|
||||
public TokenKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append("[").append(kind.toString());
|
||||
if (kind.hasPayload()) {
|
||||
s.append(":").append(data);
|
||||
}
|
||||
s.append("]");
|
||||
s.append("(").append(startpos).append(",").append(endpos).append(")");
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public boolean isIdentifier() {
|
||||
return 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;
|
||||
}
|
||||
|
||||
public String stringValue() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public Token asInstanceOfToken() {
|
||||
return new Token(TokenKind.INSTANCEOF,startpos,endpos);
|
||||
}
|
||||
|
||||
public Token asMatchesToken() {
|
||||
return new Token(TokenKind.MATCHES,startpos,endpos);
|
||||
}
|
||||
|
||||
public Token asBetweenToken() {
|
||||
return new Token(TokenKind.BETWEEN,startpos,endpos);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.expression.spel.standard;
|
||||
|
||||
/**
|
||||
* @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("@")
|
||||
;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private TokenKind() {
|
||||
this("");
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.name()+(tokenChars.length!=0?"("+new String(tokenChars)+")":"");
|
||||
}
|
||||
|
||||
public boolean hasPayload() {
|
||||
return hasPayload;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return tokenChars.length;
|
||||
}
|
||||
}
|
||||
@@ -1,507 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.standard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.expression.spel.InternalParseException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelParseException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Lex some input data into a stream of tokens that can then be parsed.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
class Tokenizer {
|
||||
|
||||
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.pos = 0;
|
||||
process();
|
||||
}
|
||||
|
||||
public void process() {
|
||||
while (pos<max) {
|
||||
char ch = toProcess[pos];
|
||||
if (isAlphabetic(ch)) {
|
||||
lexIdentifier();
|
||||
} else {
|
||||
switch (ch) {
|
||||
case '+':
|
||||
pushCharToken(TokenKind.PLUS);
|
||||
break;
|
||||
case '_': // the other way to start an identifier
|
||||
lexIdentifier();
|
||||
break;
|
||||
case '-':
|
||||
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.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
|
||||
pos++;
|
||||
break;
|
||||
case '\'':
|
||||
lexQuotedStringLiteral();
|
||||
break;
|
||||
case '"':
|
||||
lexDoubleQuotedStringLiteral();
|
||||
break;
|
||||
case 0:
|
||||
// hit sentinel at end of value
|
||||
pos++; // will take us to the end
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Cannot handle ("+Integer.valueOf(ch)+") '"+ch+"'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Token> getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
// STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
|
||||
private void lexQuotedStringLiteral() {
|
||||
int start = pos;
|
||||
boolean terminated = false;
|
||||
while (!terminated) {
|
||||
pos++;
|
||||
char ch = toProcess[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 {
|
||||
terminated = true;
|
||||
}
|
||||
}
|
||||
if (ch==0) {
|
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NON_TERMINATING_QUOTED_STRING));
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, pos));
|
||||
}
|
||||
|
||||
// DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
|
||||
private void lexDoubleQuotedStringLiteral() {
|
||||
int start = pos;
|
||||
boolean terminated = false;
|
||||
while (!terminated) {
|
||||
pos++;
|
||||
char ch = toProcess[pos];
|
||||
if (ch=='"') {
|
||||
terminated = true;
|
||||
}
|
||||
if (ch==0) {
|
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING));
|
||||
}
|
||||
}
|
||||
pos++;
|
||||
tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, 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)?;
|
||||
|
||||
private void lexNumericLiteral(boolean firstCharIsZero) {
|
||||
boolean isReal = false;
|
||||
int start = pos;
|
||||
char ch = toProcess[pos+1];
|
||||
boolean isHex = ch=='x' || ch=='X';
|
||||
|
||||
// deal with hexadecimal
|
||||
if (firstCharIsZero && isHex) {
|
||||
pos=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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// real numbers must have leading digits
|
||||
|
||||
// Consume first part of number
|
||||
do {
|
||||
pos++;
|
||||
} while (isDigit(toProcess[pos]));
|
||||
|
||||
// a '.' indicates this number is a real
|
||||
ch = toProcess[pos];
|
||||
if (ch=='.') {
|
||||
isReal = true;
|
||||
// carry on consuming digits
|
||||
do {
|
||||
pos++;
|
||||
} while (isDigit(toProcess[pos]));
|
||||
}
|
||||
|
||||
int endOfNumber = pos;
|
||||
|
||||
// Now there may or may not be an exponent
|
||||
|
||||
// is it a long ?
|
||||
if (isChar('L','l')) {
|
||||
if (isReal) { // 3.4L - not allowed
|
||||
throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.REAL_CANNOT_BE_LONG));
|
||||
}
|
||||
pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber);
|
||||
pos++;
|
||||
} else if (isExponentChar(toProcess[pos])) {
|
||||
isReal = true; // if it wasnt before, it is now
|
||||
pos++;
|
||||
char possibleSign = toProcess[pos];
|
||||
if (isSign(possibleSign)) {
|
||||
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;
|
||||
}
|
||||
pushRealToken(subarray(start,pos), isFloat, start, pos);
|
||||
} else {
|
||||
ch = toProcess[pos];
|
||||
boolean isFloat = false;
|
||||
if (isFloatSuffix(ch)) {
|
||||
isReal = true;
|
||||
isFloat = true;
|
||||
endOfNumber = ++pos;
|
||||
} else if (isDoubleSuffix(ch)) {
|
||||
isReal = true;
|
||||
endOfNumber = ++pos;
|
||||
}
|
||||
if (isReal) {
|
||||
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;
|
||||
do {
|
||||
pos++;
|
||||
} while (isIdentifier(toProcess[pos]));
|
||||
char[] subarray = subarray(start,pos);
|
||||
|
||||
// Check if this is the alternative (textual) representation of an operator (see alternativeOperatorNames)
|
||||
if ((pos-start)==2 || (pos-start)==3) {
|
||||
String asString = new String(subarray).toUpperCase();
|
||||
int idx = Arrays.binarySearch(alternativeOperatorNames,asString);
|
||||
if (idx>=0) {
|
||||
pushOneCharOrTwoCharToken(TokenKind.valueOf(asString),start,subarray);
|
||||
return;
|
||||
}
|
||||
}
|
||||
tokens.add(new Token(TokenKind.IDENTIFIER,subarray,start,pos));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
if (isLong) {
|
||||
tokens.add(new Token(TokenKind.LITERAL_HEXLONG, data, start, end));
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
private char[] subarray(int start, int end) {
|
||||
char[] result = new char[end - start];
|
||||
System.arraycopy(toProcess, start, result, 0, end - start);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this might be a two character token.
|
||||
*/
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a token of just one character in length.
|
||||
*/
|
||||
private void pushCharToken(TokenKind kind) {
|
||||
tokens.add(new Token(kind,pos,pos+1));
|
||||
pos++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a token of two characters in length.
|
||||
*/
|
||||
private void pushPairToken(TokenKind kind) {
|
||||
tokens.add(new Token(kind,pos,pos+2));
|
||||
pos+=2;
|
||||
}
|
||||
|
||||
private void pushOneCharOrTwoCharToken(TokenKind kind, int pos, char[] data) {
|
||||
tokens.add(new Token(kind,data,pos,pos+kind.getLength()));
|
||||
}
|
||||
|
||||
// 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=='$';
|
||||
}
|
||||
|
||||
private boolean isChar(char a,char b) {
|
||||
char ch = toProcess[pos];
|
||||
return ch==a || ch==b;
|
||||
}
|
||||
|
||||
private boolean isExponentChar(char ch) {
|
||||
return ch=='e' || ch=='E';
|
||||
}
|
||||
|
||||
private boolean isFloatSuffix(char ch) {
|
||||
return ch=='f' || ch=='F';
|
||||
}
|
||||
|
||||
private boolean isDoubleSuffix(char ch) {
|
||||
return ch=='d' || ch=='D';
|
||||
}
|
||||
|
||||
private boolean isSign(char ch) {
|
||||
return ch=='+' || ch=='-';
|
||||
}
|
||||
|
||||
private boolean isDigit(char ch) {
|
||||
if (ch>255) {
|
||||
return false;
|
||||
}
|
||||
return (flags[ch] & IS_DIGIT)!=0;
|
||||
}
|
||||
|
||||
private boolean isAlphabetic(char ch) {
|
||||
if (ch>255) {
|
||||
return false;
|
||||
}
|
||||
return (flags[ch] & IS_ALPHA)!=0;
|
||||
}
|
||||
|
||||
private boolean isHexadecimalDigit(char ch) {
|
||||
if (ch>255) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import org.springframework.expression.TypedValue;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
*/
|
||||
public class BooleanTypedValue extends TypedValue {
|
||||
|
||||
public static final BooleanTypedValue TRUE = new BooleanTypedValue(true);
|
||||
|
||||
public static final BooleanTypedValue FALSE = new BooleanTypedValue(false);
|
||||
|
||||
|
||||
private BooleanTypedValue(boolean b) {
|
||||
super(b);
|
||||
}
|
||||
|
||||
|
||||
public static BooleanTypedValue forValue(boolean b) {
|
||||
if (b) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,507 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* Utility methods used by the reflection resolver code to discover the appropriate
|
||||
* methods/constructors and fields that should be used in expressions.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ReflectionHelper {
|
||||
|
||||
/**
|
||||
* Compare argument arrays and return information about whether they match. A supplied type converter
|
||||
* and conversionAllowed flag allow for matches to take into account that a type may be transformed
|
||||
* into a different type by the converter.
|
||||
* @param expectedArgTypes the array of types the method/constructor is expecting
|
||||
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
|
||||
* @param typeConverter a registered type converter
|
||||
* @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) {
|
||||
|
||||
Assert.isTrue(expectedArgTypes.size() == suppliedArgTypes.size(),
|
||||
"Expected argument types and supplied argument types should be arrays of same length");
|
||||
|
||||
ArgsMatchKind match = ArgsMatchKind.EXACT;
|
||||
List<Integer> argsRequiringConversion = null;
|
||||
for (int i = 0; i < expectedArgTypes.size() && match != null; i++) {
|
||||
TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
|
||||
TypeDescriptor expectedArg = expectedArgTypes.get(i);
|
||||
if (!expectedArg.equals(suppliedArg)) {
|
||||
// The user may supply null - and that will be ok unless a primitive is expected
|
||||
if (suppliedArg == null) {
|
||||
if (expectedArg.isPrimitive()) {
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (suppliedArg.isAssignableTo(expectedArg)) {
|
||||
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
match = ArgsMatchKind.CLOSE;
|
||||
}
|
||||
}
|
||||
else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
|
||||
if (argsRequiringConversion == null) {
|
||||
argsRequiringConversion = new ArrayList<Integer>();
|
||||
}
|
||||
argsRequiringConversion.add(i);
|
||||
match = ArgsMatchKind.REQUIRES_CONVERSION;
|
||||
}
|
||||
else {
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
int[] argsArray = new int[argsRequiringConversion.size()];
|
||||
for (int i = 0; i < argsRequiringConversion.size(); i++) {
|
||||
argsArray[i] = argsRequiringConversion.get(i);
|
||||
}
|
||||
return new ArgumentsMatchInfo(match, argsArray);
|
||||
}
|
||||
else {
|
||||
return new ArgumentsMatchInfo(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on {@link MethodInvoker#getTypeDifferenceWeight(Class[], Object[])} but operates on TypeDescriptors.
|
||||
*/
|
||||
public static int getTypeDifferenceWeight(List<TypeDescriptor> paramTypes, List<TypeDescriptor> argTypes) {
|
||||
int result = 0;
|
||||
for (int i = 0,max=paramTypes.size(); i < max; i++) {
|
||||
TypeDescriptor argType = argTypes.get(i);
|
||||
TypeDescriptor paramType = paramTypes.get(i);
|
||||
if (argType == null) {
|
||||
if (paramType.isPrimitive()) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
if (!ClassUtils.isAssignable(paramType.getClass(), argType.getClass())) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
if (argType != null) {
|
||||
Class paramTypeClazz = paramType.getType();
|
||||
if (paramTypeClazz.isPrimitive()) {
|
||||
paramTypeClazz = Object.class;
|
||||
}
|
||||
Class superClass = argType.getClass().getSuperclass();
|
||||
while (superClass != null) {
|
||||
if (paramType.equals(superClass)) {
|
||||
result = result + 2;
|
||||
superClass = null;
|
||||
}
|
||||
else if (ClassUtils.isAssignable(paramTypeClazz, superClass)) {
|
||||
result = result + 2;
|
||||
superClass = superClass.getSuperclass();
|
||||
}
|
||||
else {
|
||||
superClass = null;
|
||||
}
|
||||
}
|
||||
if (paramTypeClazz.isInterface()) {
|
||||
result = result + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare argument arrays and return information about whether they match. A supplied type converter and
|
||||
* conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
|
||||
* type by the converter. This variant of compareArguments also allows for a varargs match.
|
||||
* @param expectedArgTypes the array of types the method/constructor is expecting
|
||||
* @param suppliedArgTypes the array of types that are being supplied at the point of invocation
|
||||
* @param typeConverter a registered type converter
|
||||
* @return a MatchInfo object indicating what kind of match it was or null if it was not a match
|
||||
*/
|
||||
static ArgumentsMatchInfo compareArgumentsVarargs(
|
||||
List<TypeDescriptor> expectedArgTypes, List<TypeDescriptor> suppliedArgTypes, TypeConverter typeConverter) {
|
||||
|
||||
Assert.isTrue(expectedArgTypes != null && expectedArgTypes.size() > 0,
|
||||
"Expected arguments must at least include one array (the vargargs parameter)");
|
||||
Assert.isTrue(expectedArgTypes.get(expectedArgTypes.size() - 1).isArray(),
|
||||
"Final expected argument should be array type (the varargs parameter)");
|
||||
|
||||
ArgsMatchKind match = ArgsMatchKind.EXACT;
|
||||
List<Integer> argsRequiringConversion = null;
|
||||
|
||||
// Check up until the varargs argument:
|
||||
|
||||
// Deal with the arguments up to 'expected number' - 1 (that is everything but the varargs argument)
|
||||
int argCountUpToVarargs = expectedArgTypes.size() - 1;
|
||||
for (int i = 0; i < argCountUpToVarargs && match != null; i++) {
|
||||
TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
|
||||
TypeDescriptor expectedArg = expectedArgTypes.get(i);
|
||||
if (suppliedArg == null) {
|
||||
if (expectedArg.isPrimitive()) {
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!expectedArg.equals(suppliedArg)) {
|
||||
if (suppliedArg.isAssignableTo(expectedArg)) {
|
||||
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
match = ArgsMatchKind.CLOSE;
|
||||
}
|
||||
}
|
||||
else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
|
||||
if (argsRequiringConversion == null) {
|
||||
argsRequiringConversion = new ArrayList<Integer>();
|
||||
}
|
||||
argsRequiringConversion.add(i);
|
||||
match = ArgsMatchKind.REQUIRES_CONVERSION;
|
||||
}
|
||||
else {
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If already confirmed it cannot be a match, then return
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (suppliedArgTypes.size() == expectedArgTypes.size() &&
|
||||
expectedArgTypes.get(expectedArgTypes.size() - 1).equals(
|
||||
suppliedArgTypes.get(suppliedArgTypes.size() - 1))) {
|
||||
// Special case: there is one parameter left and it is an array and it matches the varargs
|
||||
// expected argument - that is a match, the caller has already built the array. Proceed with it.
|
||||
}
|
||||
else {
|
||||
// Now... we have the final argument in the method we are checking as a match and we have 0 or more other
|
||||
// arguments left to pass to it.
|
||||
Class varargsParameterType = expectedArgTypes.get(expectedArgTypes.size() - 1).getElementTypeDescriptor().getType();
|
||||
|
||||
// All remaining parameters must be of this type or convertable to this type
|
||||
for (int i = expectedArgTypes.size() - 1; i < suppliedArgTypes.size(); i++) {
|
||||
TypeDescriptor suppliedArg = suppliedArgTypes.get(i);
|
||||
if (suppliedArg == null) {
|
||||
if (varargsParameterType.isPrimitive()) {
|
||||
match = null;
|
||||
}
|
||||
} else {
|
||||
if (varargsParameterType != suppliedArg.getType()) {
|
||||
if (ClassUtils.isAssignable(varargsParameterType, suppliedArg.getType())) {
|
||||
if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
match = ArgsMatchKind.CLOSE;
|
||||
}
|
||||
}
|
||||
else if (typeConverter.canConvert(suppliedArg, TypeDescriptor.valueOf(varargsParameterType))) {
|
||||
if (argsRequiringConversion == null) {
|
||||
argsRequiringConversion = new ArrayList<Integer>();
|
||||
}
|
||||
argsRequiringConversion.add(i);
|
||||
match = ArgsMatchKind.REQUIRES_CONVERSION;
|
||||
}
|
||||
else {
|
||||
match = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
int[] argsArray = new int[argsRequiringConversion.size()];
|
||||
for (int i = 0; i < argsRequiringConversion.size(); i++) {
|
||||
argsArray[i] = argsRequiringConversion.get(i);
|
||||
}
|
||||
return new ArgumentsMatchInfo(match, argsArray);
|
||||
}
|
||||
else {
|
||||
return new ArgumentsMatchInfo(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an input set of argument values and, following the positions specified in the int array,
|
||||
* it converts them to the types specified as the required parameter types. The arguments are
|
||||
* converted 'in-place' in the input array.
|
||||
* @param converter the type converter to use for attempting conversions
|
||||
* @param arguments the actual arguments that need conversion
|
||||
* @param methodOrCtor the target Method or Constructor
|
||||
* @param argumentsRequiringConversion details which of the input arguments for sure need conversion
|
||||
* @param varargsPosition the known position of the varargs argument, if any
|
||||
* @throws EvaluationException if a problem occurs during conversion
|
||||
*/
|
||||
static void convertArguments(TypeConverter converter, Object[] arguments, Object methodOrCtor,
|
||||
int[] argumentsRequiringConversion, Integer varargsPosition) throws EvaluationException {
|
||||
if (varargsPosition == null) {
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
TypeDescriptor targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, i));
|
||||
Object argument = arguments[i];
|
||||
arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < varargsPosition; i++) {
|
||||
TypeDescriptor targetType = new TypeDescriptor(MethodParameter.forMethodOrConstructor(methodOrCtor, i));
|
||||
Object argument = arguments[i];
|
||||
arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
|
||||
}
|
||||
MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, varargsPosition);
|
||||
if (varargsPosition == arguments.length - 1) {
|
||||
TypeDescriptor targetType = new TypeDescriptor(methodParam);
|
||||
Object argument = arguments[varargsPosition];
|
||||
arguments[varargsPosition] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
|
||||
} else {
|
||||
TypeDescriptor targetType = TypeDescriptor.nested(methodParam, 1);
|
||||
for (int i = varargsPosition; i < arguments.length; i++) {
|
||||
Object argument = arguments[i];
|
||||
arguments[i] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a supplied set of arguments into the requested types. If the parameterTypes are related to
|
||||
* a varargs method then the final entry in the parameterTypes array is going to be an array itself whose
|
||||
* component type should be used as the conversion target for extraneous arguments. (For example, if the
|
||||
* parameterTypes are {Integer, String[]} and the input arguments are {Integer, boolean, float} then both
|
||||
* the boolean and float must be converted to strings). This method does not repackage the arguments
|
||||
* into a form suitable for the varargs invocation
|
||||
* @param converter the converter to use for type conversions
|
||||
* @param arguments the arguments to convert to the requested parameter types
|
||||
* @param method the target Method
|
||||
* @throws SpelEvaluationException if there is a problem with conversion
|
||||
*/
|
||||
public static void convertAllArguments(TypeConverter converter, Object[] arguments, Method method) throws SpelEvaluationException {
|
||||
Integer varargsPosition = null;
|
||||
if (method.isVarArgs()) {
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
varargsPosition = paramTypes.length - 1;
|
||||
}
|
||||
for (int argPosition = 0; argPosition < arguments.length; argPosition++) {
|
||||
TypeDescriptor targetType;
|
||||
if (varargsPosition != null && argPosition >= varargsPosition) {
|
||||
MethodParameter methodParam = new MethodParameter(method, varargsPosition);
|
||||
targetType = TypeDescriptor.nested(methodParam, 1);
|
||||
}
|
||||
else {
|
||||
targetType = new TypeDescriptor(new MethodParameter(method, argPosition));
|
||||
}
|
||||
try {
|
||||
Object argument = arguments[argPosition];
|
||||
if (argument != null && !targetType.getObjectType().isInstance(argument)) {
|
||||
if (converter == null) {
|
||||
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, argument.getClass().getName(), targetType);
|
||||
}
|
||||
arguments[argPosition] = converter.convertValue(argument, TypeDescriptor.forObject(argument), targetType);
|
||||
}
|
||||
}
|
||||
catch (EvaluationException ex) {
|
||||
// allows for another type converter throwing a different kind of EvaluationException
|
||||
if (ex instanceof SpelEvaluationException) {
|
||||
throw (SpelEvaluationException)ex;
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(ex, SpelMessage.TYPE_CONVERSION_ERROR,arguments[argPosition].getClass().getName(), targetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Package up the arguments so that they correctly match what is expected in parameterTypes. For example, if
|
||||
* parameterTypes is (int, String[]) because the second parameter was declared String... then if arguments is
|
||||
* [1,"a","b"] then it must be repackaged as [1,new String[]{"a","b"}] in order to match the expected
|
||||
* parameterTypes.
|
||||
* @param requiredParameterTypes the types of the parameters for the invocation
|
||||
* @param args the arguments to be setup ready for the invocation
|
||||
* @return a repackaged array of arguments where any varargs setup has been done
|
||||
*/
|
||||
public static Object[] setupArgumentsForVarargsInvocation(Class[] requiredParameterTypes, Object... args) {
|
||||
// Check if array already built for final argument
|
||||
int parameterCount = requiredParameterTypes.length;
|
||||
int argumentCount = args.length;
|
||||
|
||||
// Check if repackaging is needed:
|
||||
if (parameterCount != args.length ||
|
||||
requiredParameterTypes[parameterCount - 1] !=
|
||||
(args[argumentCount - 1] == null ? null : args[argumentCount - 1].getClass())) {
|
||||
int arraySize = 0; // zero size array if nothing to pass as the varargs parameter
|
||||
if (argumentCount >= parameterCount) {
|
||||
arraySize = argumentCount - (parameterCount - 1);
|
||||
}
|
||||
|
||||
// Create an array for the varargs arguments
|
||||
Object[] newArgs = new Object[parameterCount];
|
||||
for (int i = 0; i < newArgs.length - 1; i++) {
|
||||
newArgs[i] = args[i];
|
||||
}
|
||||
// Now sort out the final argument, which is the varargs one. Before entering this
|
||||
// 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);
|
||||
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();
|
||||
}
|
||||
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];
|
||||
}
|
||||
newArgs[newArgs.length - 1] = repackagedArguments;
|
||||
}
|
||||
return newArgs;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
public static enum ArgsMatchKind {
|
||||
// An exact match is where the parameter types exactly match what the method/constructor being invoked is expecting
|
||||
EXACT,
|
||||
// A close match is where the parameter types either exactly match or are assignment compatible with the method/constructor being invoked
|
||||
CLOSE,
|
||||
// A conversion match is where the type converter must be used to transform some of the parameter types
|
||||
REQUIRES_CONVERSION
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An instance of ArgumentsMatchInfo describes what kind of match was achieved between two sets of arguments - the set that a
|
||||
* method/constructor is expecting and the set that are being supplied at the point of invocation. If the kind
|
||||
* indicates that conversion is required for some of the arguments then the arguments that require conversion are
|
||||
* listed in the argsRequiringConversion array.
|
||||
*/
|
||||
public static class ArgumentsMatchInfo {
|
||||
|
||||
public final ArgsMatchKind kind;
|
||||
|
||||
public int[] argsRequiringConversion;
|
||||
|
||||
ArgumentsMatchInfo(ArgsMatchKind kind, int[] integers) {
|
||||
this.kind = kind;
|
||||
this.argsRequiringConversion = integers;
|
||||
}
|
||||
|
||||
ArgumentsMatchInfo(ArgsMatchKind kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public boolean isExactMatch() {
|
||||
return (this.kind == ArgsMatchKind.EXACT);
|
||||
}
|
||||
|
||||
public boolean isCloseMatch() {
|
||||
return (this.kind == ArgsMatchKind.CLOSE);
|
||||
}
|
||||
|
||||
public boolean isMatchRequiringConversion() {
|
||||
return (this.kind == ArgsMatchKind.REQUIRES_CONVERSION);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("ArgumentMatch: ").append(this.kind);
|
||||
if (this.argsRequiringConversion != null) {
|
||||
sb.append(" (argsForConversion:");
|
||||
for (int i = 0; i < this.argsRequiringConversion.length;i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(this.argsRequiringConversion[i]);
|
||||
}
|
||||
sb.append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.ConstructorExecutor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* A simple ConstructorExecutor implementation that runs a constructor using reflective invocation.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
class ReflectiveConstructorExecutor implements ConstructorExecutor {
|
||||
|
||||
private final Constructor<?> ctor;
|
||||
|
||||
private final Integer varargsPosition;
|
||||
|
||||
// When the constructor was found, we will have determined if arguments need to be converted for it
|
||||
// to be invoked. Conversion won't be cheap so let's only do it if necessary.
|
||||
private final int[] argsRequiringConversion;
|
||||
|
||||
|
||||
public ReflectiveConstructorExecutor(Constructor<?> ctor, int[] argsRequiringConversion) {
|
||||
this.ctor = ctor;
|
||||
if (ctor.isVarArgs()) {
|
||||
Class[] paramTypes = ctor.getParameterTypes();
|
||||
this.varargsPosition = paramTypes.length - 1;
|
||||
}
|
||||
else {
|
||||
this.varargsPosition = null;
|
||||
}
|
||||
this.argsRequiringConversion = argsRequiringConversion;
|
||||
}
|
||||
|
||||
public TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException {
|
||||
try {
|
||||
if (arguments != null) {
|
||||
ReflectionHelper.convertArguments(context.getTypeConverter(), arguments,
|
||||
this.ctor, this.argsRequiringConversion, this.varargsPosition);
|
||||
}
|
||||
if (this.ctor.isVarArgs()) {
|
||||
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.ctor.getParameterTypes(), arguments);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(this.ctor);
|
||||
return new TypedValue(this.ctor.newInstance(arguments));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Problem invoking constructor: " + this.ctor, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.ConstructorExecutor;
|
||||
import org.springframework.expression.ConstructorResolver;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
|
||||
/**
|
||||
* A constructor resolver that uses reflection to locate the constructor that should be invoked
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
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.
|
||||
* </ol>
|
||||
*/
|
||||
public ConstructorExecutor resolve(EvaluationContext context, String typename, List<TypeDescriptor> argumentTypes)
|
||||
throws AccessException {
|
||||
|
||||
try {
|
||||
TypeConverter typeConverter = context.getTypeConverter();
|
||||
Class<?> type = context.getTypeLocator().findType(typename);
|
||||
Constructor[] ctors = type.getConstructors();
|
||||
|
||||
Arrays.sort(ctors, new Comparator<Constructor>() {
|
||||
public int compare(Constructor c1, Constructor c2) {
|
||||
int c1pl = c1.getParameterTypes().length;
|
||||
int c2pl = c2.getParameterTypes().length;
|
||||
return (new Integer(c1pl)).compareTo(c2pl);
|
||||
}
|
||||
});
|
||||
|
||||
Constructor closeMatch = null;
|
||||
int[] argsToConvert = null;
|
||||
Constructor matchRequiringConversion = null;
|
||||
|
||||
for (Constructor ctor : ctors) {
|
||||
Class[] paramTypes = ctor.getParameterTypes();
|
||||
List<TypeDescriptor> paramDescriptors = new ArrayList<TypeDescriptor>(paramTypes.length);
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
paramDescriptors.add(new TypeDescriptor(new MethodParameter(ctor, i)));
|
||||
}
|
||||
ReflectionHelper.ArgumentsMatchInfo matchInfo = null;
|
||||
if (ctor.isVarArgs() && argumentTypes.size() >= (paramTypes.length - 1)) {
|
||||
// *sigh* complicated
|
||||
// Basically.. we have to have all parameters match up until the varargs one, then the rest of what is
|
||||
// being provided should be
|
||||
// the same type whilst the final argument to the method must be an array of that (oh, how easy...not) -
|
||||
// or the final parameter
|
||||
// we are supplied does match exactly (it is an array already).
|
||||
matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
else if (paramTypes.length == argumentTypes.size()) {
|
||||
// worth a closer look
|
||||
matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
if (matchInfo != null) {
|
||||
if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.EXACT) {
|
||||
return new ReflectiveConstructorExecutor(ctor, null);
|
||||
}
|
||||
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.CLOSE) {
|
||||
closeMatch = ctor;
|
||||
}
|
||||
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
argsToConvert = matchInfo.argsRequiringConversion;
|
||||
matchRequiringConversion = ctor;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closeMatch != null) {
|
||||
return new ReflectiveConstructorExecutor(closeMatch, null);
|
||||
}
|
||||
else if (matchRequiringConversion != null) {
|
||||
return new ReflectiveConstructorExecutor(matchRequiringConversion, argsToConvert);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (EvaluationException ex) {
|
||||
throw new AccessException("Failed to resolve constructor", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
class ReflectiveMethodExecutor implements MethodExecutor {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Integer varargsPosition;
|
||||
|
||||
// When the method was found, we will have determined if arguments need to be converted for it
|
||||
// to be invoked. Conversion won't be cheap so let's only do it if necessary.
|
||||
private final int[] argsRequiringConversion;
|
||||
|
||||
|
||||
public ReflectiveMethodExecutor(Method theMethod, int[] argumentsRequiringConversion) {
|
||||
this.method = theMethod;
|
||||
if (theMethod.isVarArgs()) {
|
||||
Class[] paramTypes = theMethod.getParameterTypes();
|
||||
this.varargsPosition = paramTypes.length - 1;
|
||||
}
|
||||
else {
|
||||
this.varargsPosition = null;
|
||||
}
|
||||
this.argsRequiringConversion = argumentsRequiringConversion;
|
||||
}
|
||||
|
||||
|
||||
public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
|
||||
try {
|
||||
if (arguments != null) {
|
||||
ReflectionHelper.convertArguments(
|
||||
context.getTypeConverter(), arguments, this.method,
|
||||
this.argsRequiringConversion, this.varargsPosition);
|
||||
}
|
||||
if (this.method.isVarArgs()) {
|
||||
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments);
|
||||
}
|
||||
ReflectionUtils.makeAccessible(this.method);
|
||||
Object value = this.method.invoke(target, arguments);
|
||||
return new TypedValue(value, new TypeDescriptor(new MethodParameter(this.method, -1)).narrow(value));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Problem invoking method: " + this.method, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.MethodExecutor;
|
||||
import org.springframework.expression.MethodFilter;
|
||||
import org.springframework.expression.MethodResolver;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A method resolver that uses reflection to locate the method that should be invoked.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
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,
|
||||
// more closely following the Java rules.
|
||||
private boolean useDistance = false;
|
||||
|
||||
|
||||
public ReflectiveMethodResolver() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructors allows the ReflectiveMethodResolver to be configured such that it will
|
||||
* use a distance computation to check which is the better of two close matches (when there
|
||||
* are multiple matches). Using the distance computation is intended to ensure matches
|
||||
* are more closely representative of what a Java compiler would do when taking into
|
||||
* account boxing/unboxing and whether the method candidates are declared to handle a
|
||||
* supertype of the type (of the argument) being passed in.
|
||||
* @param useDistance true if distance computation should be used when calculating matches
|
||||
*/
|
||||
public ReflectiveMethodResolver(boolean useDistance) {
|
||||
this.useDistance = useDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a method on a type. There are three kinds of match that might occur:
|
||||
* <ol>
|
||||
* <li>An exact match where the types of the arguments match the types of the constructor
|
||||
* <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor
|
||||
* <li>A match where we are able to convert the arguments into those expected by the constructor,
|
||||
* according to the registered type converter.
|
||||
* </ol>
|
||||
*/
|
||||
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,
|
||||
List<TypeDescriptor> argumentTypes) throws AccessException {
|
||||
|
||||
try {
|
||||
TypeConverter typeConverter = context.getTypeConverter();
|
||||
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
|
||||
Method[] methods = type.getMethods();
|
||||
|
||||
// If a filter is registered for this type, call it
|
||||
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
|
||||
if (filter != null) {
|
||||
List<Method> methodsForFiltering = new ArrayList<Method>();
|
||||
for (Method method: methods) {
|
||||
methodsForFiltering.add(method);
|
||||
}
|
||||
List<Method> methodsFiltered = filter.filter(methodsForFiltering);
|
||||
if (CollectionUtils.isEmpty(methodsFiltered)) {
|
||||
methods = NO_METHODS;
|
||||
}
|
||||
else {
|
||||
methods = methodsFiltered.toArray(new Method[methodsFiltered.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
Arrays.sort(methods, new Comparator<Method>() {
|
||||
public int compare(Method m1, Method m2) {
|
||||
int m1pl = m1.getParameterTypes().length;
|
||||
int m2pl = m2.getParameterTypes().length;
|
||||
return (new Integer(m1pl)).compareTo(m2pl);
|
||||
}
|
||||
});
|
||||
|
||||
Method closeMatch = null;
|
||||
int closeMatchDistance = Integer.MAX_VALUE;
|
||||
int[] argsToConvert = null;
|
||||
Method matchRequiringConversion = null;
|
||||
boolean multipleOptions = false;
|
||||
|
||||
for (Method method : methods) {
|
||||
if (method.isBridge()) {
|
||||
continue;
|
||||
}
|
||||
if (method.getName().equals(name)) {
|
||||
Class[] paramTypes = method.getParameterTypes();
|
||||
List<TypeDescriptor> paramDescriptors = new ArrayList<TypeDescriptor>(paramTypes.length);
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
|
||||
}
|
||||
ReflectionHelper.ArgumentsMatchInfo matchInfo = null;
|
||||
if (method.isVarArgs() && argumentTypes.size() >= (paramTypes.length - 1)) {
|
||||
// *sigh* complicated
|
||||
matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
else if (paramTypes.length == argumentTypes.size()) {
|
||||
// name and parameter number match, check the arguments
|
||||
matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter);
|
||||
}
|
||||
if (matchInfo != null) {
|
||||
if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.EXACT) {
|
||||
return new ReflectiveMethodExecutor(method, null);
|
||||
}
|
||||
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.CLOSE) {
|
||||
if (!useDistance) {
|
||||
closeMatch = method;
|
||||
} else {
|
||||
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
|
||||
if (matchDistance<closeMatchDistance) {
|
||||
// this is a better match
|
||||
closeMatchDistance = matchDistance;
|
||||
closeMatch = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.REQUIRES_CONVERSION) {
|
||||
if (matchRequiringConversion != null) {
|
||||
multipleOptions = true;
|
||||
}
|
||||
argsToConvert = matchInfo.argsRequiringConversion;
|
||||
matchRequiringConversion = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (closeMatch != null) {
|
||||
return new ReflectiveMethodExecutor(closeMatch, null);
|
||||
}
|
||||
else if (matchRequiringConversion != null) {
|
||||
if (multipleOptions) {
|
||||
throw new SpelEvaluationException(SpelMessage.MULTIPLE_POSSIBLE_METHODS, name);
|
||||
}
|
||||
return new ReflectiveMethodExecutor(matchRequiringConversion, argsToConvert);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (EvaluationException ex) {
|
||||
throw new AccessException("Failed to resolve method", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerMethodFilter(Class<?> type, MethodFilter filter) {
|
||||
if (this.filters == null) {
|
||||
this.filters = new HashMap<Class<?>, MethodFilter>();
|
||||
}
|
||||
if (filter == null) {
|
||||
this.filters.remove(type);
|
||||
}
|
||||
else {
|
||||
this.filters.put(type,filter);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,527 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.Property;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypedValue;
|
||||
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).
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ReflectivePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
protected final Map<CacheKey, InvokerPair> readerCache = new ConcurrentHashMap<CacheKey, InvokerPair>();
|
||||
|
||||
protected final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
|
||||
|
||||
protected final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey, TypeDescriptor>();
|
||||
|
||||
|
||||
/**
|
||||
* @return null which means this is a general purpose accessor
|
||||
*/
|
||||
public Class<?>[] getSpecificTargetClasses() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
if (type.isArray() && name.equals("length")) {
|
||||
return true;
|
||||
}
|
||||
CacheKey cacheKey = new CacheKey(type, name);
|
||||
if (this.readerCache.containsKey(cacheKey)) {
|
||||
return true;
|
||||
}
|
||||
Method method = findGetterForProperty(name, type, target instanceof Class);
|
||||
if (method != null) {
|
||||
// Treat it like a property
|
||||
// The readerCache will only contain gettable properties (let's not worry about setters for now)
|
||||
Property property = new Property(type, method, null);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
this.readerCache.put(cacheKey, new InvokerPair(method, typeDescriptor));
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Field field = findField(name, type, target instanceof Class);
|
||||
if (field != null) {
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(field);
|
||||
this.readerCache.put(cacheKey, new InvokerPair(field,typeDescriptor));
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (target == null) {
|
||||
throw new AccessException("Cannot read property of null target");
|
||||
}
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
|
||||
if (type.isArray() && name.equals("length")) {
|
||||
if (target instanceof Class) {
|
||||
throw new AccessException("Cannot access length on array class itself");
|
||||
}
|
||||
return new TypedValue(Array.getLength(target));
|
||||
}
|
||||
|
||||
CacheKey cacheKey = new CacheKey(type, name);
|
||||
InvokerPair invoker = this.readerCache.get(cacheKey);
|
||||
|
||||
if (invoker == null || invoker.member instanceof Method) {
|
||||
Method method = (Method) (invoker != null ? invoker.member : null);
|
||||
if (method == null) {
|
||||
method = findGetterForProperty(name, type, target instanceof Class);
|
||||
if (method != null) {
|
||||
// TODO remove the duplication here between canRead and read
|
||||
// Treat it like a property
|
||||
// The readerCache will only contain gettable properties (let's not worry about setters for now)
|
||||
Property property = new Property(type, method, null);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
invoker = new InvokerPair(method, typeDescriptor);
|
||||
this.readerCache.put(cacheKey, invoker);
|
||||
}
|
||||
}
|
||||
if (method != null) {
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
Object value = method.invoke(target);
|
||||
return new TypedValue(value, invoker.typeDescriptor.narrow(value));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access property '" + name + "' through getter", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (invoker == null || invoker.member instanceof Field) {
|
||||
Field field = (Field) (invoker == null ? null : invoker.member);
|
||||
if (field == null) {
|
||||
field = findField(name, type, target instanceof Class);
|
||||
if (field != null) {
|
||||
invoker = new InvokerPair(field, new TypeDescriptor(field));
|
||||
this.readerCache.put(cacheKey, invoker);
|
||||
}
|
||||
}
|
||||
if (field != null) {
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
Object value = field.get(target);
|
||||
return new TypedValue(value, invoker.typeDescriptor.narrow(value));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access field: " + name, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new AccessException("Neither getter nor field found for property '" + name + "'");
|
||||
}
|
||||
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
CacheKey cacheKey = new CacheKey(type, name);
|
||||
if (this.writerCache.containsKey(cacheKey)) {
|
||||
return true;
|
||||
}
|
||||
Method method = findSetterForProperty(name, type, target instanceof Class);
|
||||
if (method != null) {
|
||||
// Treat it like a property
|
||||
Property property = new Property(type, null, method);
|
||||
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
|
||||
this.writerCache.put(cacheKey, method);
|
||||
this.typeDescriptorCache.put(cacheKey, typeDescriptor);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
Field field = findField(name, type, target instanceof Class);
|
||||
if (field != null) {
|
||||
this.writerCache.put(cacheKey, field);
|
||||
this.typeDescriptorCache.put(cacheKey, new TypeDescriptor(field));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||
if (target == null) {
|
||||
throw new AccessException("Cannot write property on null target");
|
||||
}
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
|
||||
Object possiblyConvertedNewValue = newValue;
|
||||
TypeDescriptor typeDescriptor = getTypeDescriptor(context, target, name);
|
||||
if (typeDescriptor != null) {
|
||||
try {
|
||||
possiblyConvertedNewValue = context.getTypeConverter().convertValue(
|
||||
newValue, TypeDescriptor.forObject(newValue), typeDescriptor);
|
||||
}
|
||||
catch (EvaluationException evaluationException) {
|
||||
throw new AccessException("Type conversion failure",evaluationException);
|
||||
}
|
||||
}
|
||||
CacheKey cacheKey = new CacheKey(type, name);
|
||||
Member cachedMember = this.writerCache.get(cacheKey);
|
||||
|
||||
if (cachedMember == null || cachedMember instanceof Method) {
|
||||
Method method = (Method) cachedMember;
|
||||
if (method == null) {
|
||||
method = findSetterForProperty(name, type, target instanceof Class);
|
||||
if (method != null) {
|
||||
cachedMember = method;
|
||||
this.writerCache.put(cacheKey, cachedMember);
|
||||
}
|
||||
}
|
||||
if (method != null) {
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
method.invoke(target, possiblyConvertedNewValue);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access property '" + name + "' through setter", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedMember == null || cachedMember instanceof Field) {
|
||||
Field field = (Field) cachedMember;
|
||||
if (field == null) {
|
||||
field = findField(name, type, target instanceof Class);
|
||||
if (field != null) {
|
||||
cachedMember = field;
|
||||
this.writerCache.put(cacheKey, cachedMember);
|
||||
}
|
||||
}
|
||||
if (field != null) {
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
field.set(target, possiblyConvertedNewValue);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access field: " + name, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new AccessException("Neither setter nor field found for property '" + name + "'");
|
||||
}
|
||||
|
||||
private TypeDescriptor getTypeDescriptor(EvaluationContext context, Object target, String name) {
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
|
||||
if (type.isArray() && name.equals("length")) {
|
||||
return TypeDescriptor.valueOf(Integer.TYPE);
|
||||
}
|
||||
CacheKey cacheKey = new CacheKey(type, name);
|
||||
TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
if (typeDescriptor == null) {
|
||||
// attempt to populate the cache entry
|
||||
try {
|
||||
if (canRead(context, target, name)) {
|
||||
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
}
|
||||
else if (canWrite(context, target, name)) {
|
||||
typeDescriptor = this.typeDescriptorCache.get(cacheKey);
|
||||
}
|
||||
}
|
||||
catch (AccessException ex) {
|
||||
// continue with null type descriptor
|
||||
}
|
||||
}
|
||||
return typeDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
|
||||
* 'get' and the rest of the name is the same as the property name (with the first character uppercased).
|
||||
*/
|
||||
protected Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
||||
Method[] ms = clazz.getMethods();
|
||||
// Try "get*" method...
|
||||
String getterName = "get" + StringUtils.capitalize(propertyName);
|
||||
for (Method method : ms) {
|
||||
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
|
||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
// Try "is*" method...
|
||||
getterName = "is" + StringUtils.capitalize(propertyName);
|
||||
for (Method method : ms) {
|
||||
if (method.getName().equals(getterName) && method.getParameterTypes().length == 0 &&
|
||||
boolean.class.equals(method.getReturnType()) &&
|
||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a setter method for the specified property.
|
||||
*/
|
||||
protected Method findSetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
|
||||
Method[] methods = clazz.getMethods();
|
||||
String setterName = "set" + StringUtils.capitalize(propertyName);
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(setterName) && method.getParameterTypes().length == 1 &&
|
||||
(!mustBeStatic || Modifier.isStatic(method.getModifiers()))) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a field of a certain name on a specified class
|
||||
*/
|
||||
protected Field findField(String name, Class<?> clazz, boolean mustBeStatic) {
|
||||
Field[] fields = clazz.getFields();
|
||||
for (Field field : fields) {
|
||||
if (field.getName().equals(name) && (!mustBeStatic || Modifier.isStatic(field.getModifiers()))) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the member (method/field) to call reflectively to access a property value and the type descriptor for the
|
||||
* value returned by the reflective call.
|
||||
*/
|
||||
private static class InvokerPair {
|
||||
|
||||
final Member member;
|
||||
|
||||
final TypeDescriptor typeDescriptor;
|
||||
|
||||
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
|
||||
this.member = member;
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CacheKey {
|
||||
|
||||
private final Class clazz;
|
||||
|
||||
private final String name;
|
||||
|
||||
public CacheKey(Class clazz, String name) {
|
||||
this.clazz = clazz;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof CacheKey)) {
|
||||
return false;
|
||||
}
|
||||
CacheKey otherKey = (CacheKey) other;
|
||||
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.clazz.hashCode() * 29 + this.name.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to create an optimized property accessor tailored for a property of a particular name on
|
||||
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
|
||||
* due to the need to lookup which reflective member (method/field) to use each time read() is called.
|
||||
* This method will just return the ReflectivePropertyAccessor instance if it is unable to build
|
||||
* something more optimal.
|
||||
*/
|
||||
public PropertyAccessor createOptimalAccessor(EvaluationContext eContext, Object target, String name) {
|
||||
// Don't be clever for arrays or null target
|
||||
if (target == null) {
|
||||
return this;
|
||||
}
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
if (type.isArray()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
CacheKey cacheKey = new CacheKey(type, name);
|
||||
InvokerPair invocationTarget = this.readerCache.get(cacheKey);
|
||||
|
||||
if (invocationTarget == null || invocationTarget.member instanceof Method) {
|
||||
Method method = (Method) (invocationTarget==null?null:invocationTarget.member);
|
||||
if (method == null) {
|
||||
method = findGetterForProperty(name, type, target instanceof Class);
|
||||
if (method != null) {
|
||||
invocationTarget = new InvokerPair(method,new TypeDescriptor(new MethodParameter(method,-1)));
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
this.readerCache.put(cacheKey, invocationTarget);
|
||||
}
|
||||
}
|
||||
if (method != null) {
|
||||
return new OptimalPropertyAccessor(invocationTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if (invocationTarget == null || invocationTarget.member instanceof Field) {
|
||||
Field field = (Field) (invocationTarget==null?null:invocationTarget.member);
|
||||
if (field == null) {
|
||||
field = findField(name, type, target instanceof Class);
|
||||
if (field != null) {
|
||||
invocationTarget = new InvokerPair(field, new TypeDescriptor(field));
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
this.readerCache.put(cacheKey, invocationTarget);
|
||||
}
|
||||
}
|
||||
if (field != null) {
|
||||
return new OptimalPropertyAccessor(invocationTarget);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* An optimized form of a PropertyAccessor that will use reflection but only knows how to access a particular property
|
||||
* on a particular class. This is unlike the general ReflectivePropertyResolver which manages a cache of methods/fields that
|
||||
* may be invoked to access different properties on different classes. This optimal accessor exists because looking up
|
||||
* the appropriate reflective object by class/name on each read is not cheap.
|
||||
*/
|
||||
static class OptimalPropertyAccessor implements PropertyAccessor {
|
||||
private final Member member;
|
||||
private final TypeDescriptor typeDescriptor;
|
||||
private final boolean needsToBeMadeAccessible;
|
||||
|
||||
OptimalPropertyAccessor(InvokerPair target) {
|
||||
this.member = target.member;
|
||||
this.typeDescriptor = target.typeDescriptor;
|
||||
if (this.member instanceof Field) {
|
||||
Field field = (Field)member;
|
||||
needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
|
||||
&& !field.isAccessible();
|
||||
}
|
||||
else {
|
||||
Method method = (Method)member;
|
||||
needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible());
|
||||
}
|
||||
}
|
||||
|
||||
public Class[] getSpecificTargetClasses() {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass());
|
||||
if (type.isArray()) {
|
||||
return false;
|
||||
}
|
||||
if (member instanceof Method) {
|
||||
Method method = (Method)member;
|
||||
String getterName = "get" + StringUtils.capitalize(name);
|
||||
if (getterName.equals(method.getName())) {
|
||||
return true;
|
||||
}
|
||||
getterName = "is" + StringUtils.capitalize(name);
|
||||
return getterName.equals(method.getName());
|
||||
}
|
||||
else {
|
||||
Field field = (Field)member;
|
||||
return field.getName().equals(name);
|
||||
}
|
||||
}
|
||||
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
if (member instanceof Method) {
|
||||
try {
|
||||
if (needsToBeMadeAccessible) {
|
||||
ReflectionUtils.makeAccessible((Method) member);
|
||||
}
|
||||
Object value = ((Method) member).invoke(target);
|
||||
return new TypedValue(value, typeDescriptor.narrow(value));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access property '" + name + "' through getter", ex);
|
||||
}
|
||||
}
|
||||
if (member instanceof Field) {
|
||||
try {
|
||||
if (needsToBeMadeAccessible) {
|
||||
ReflectionUtils.makeAccessible((Field)member);
|
||||
}
|
||||
Object value = ((Field)member).get(target);
|
||||
return new TypedValue(value, typeDescriptor.narrow(value));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new AccessException("Unable to access field: " + name, ex);
|
||||
}
|
||||
}
|
||||
throw new AccessException("Neither getter nor field found for property '" + name + "'");
|
||||
}
|
||||
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue)
|
||||
throws AccessException {
|
||||
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.expression.ConstructorResolver;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.MethodFilter;
|
||||
import org.springframework.expression.MethodResolver;
|
||||
import org.springframework.expression.OperatorOverloader;
|
||||
import org.springframework.expression.PropertyAccessor;
|
||||
import org.springframework.expression.TypeComparator;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypeLocator;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Provides a default EvaluationContext implementation.
|
||||
*
|
||||
* <p>To resolve properties/methods/fields this context uses a reflection mechanism.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StandardEvaluationContext implements EvaluationContext {
|
||||
|
||||
private TypedValue rootObject;
|
||||
|
||||
private List<ConstructorResolver> constructorResolvers;
|
||||
|
||||
private List<MethodResolver> methodResolvers;
|
||||
|
||||
private ReflectiveMethodResolver reflectiveMethodResolver;
|
||||
|
||||
private List<PropertyAccessor> propertyAccessors;
|
||||
|
||||
private TypeLocator typeLocator;
|
||||
|
||||
private TypeConverter typeConverter;
|
||||
|
||||
private TypeComparator typeComparator = new StandardTypeComparator();
|
||||
|
||||
private OperatorOverloader operatorOverloader = new StandardOperatorOverloader();
|
||||
|
||||
private final Map<String, Object> variables = new HashMap<String, Object>();
|
||||
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
|
||||
public StandardEvaluationContext() {
|
||||
setRootObject(null);
|
||||
}
|
||||
|
||||
public StandardEvaluationContext(Object rootObject) {
|
||||
this();
|
||||
setRootObject(rootObject);
|
||||
}
|
||||
|
||||
|
||||
public void setRootObject(Object rootObject, TypeDescriptor typeDescriptor) {
|
||||
this.rootObject = new TypedValue(rootObject, typeDescriptor);
|
||||
}
|
||||
|
||||
public void setRootObject(Object rootObject) {
|
||||
this.rootObject = (rootObject != null ? new TypedValue(rootObject) : TypedValue.NULL);
|
||||
}
|
||||
|
||||
public TypedValue getRootObject() {
|
||||
return this.rootObject;
|
||||
}
|
||||
|
||||
public void addConstructorResolver(ConstructorResolver resolver) {
|
||||
ensureConstructorResolversInitialized();
|
||||
this.constructorResolvers.add(this.constructorResolvers.size() - 1, resolver);
|
||||
}
|
||||
|
||||
public boolean removeConstructorResolver(ConstructorResolver resolver) {
|
||||
ensureConstructorResolversInitialized();
|
||||
return this.constructorResolvers.remove(resolver);
|
||||
}
|
||||
|
||||
public List<ConstructorResolver> getConstructorResolvers() {
|
||||
ensureConstructorResolversInitialized();
|
||||
return this.constructorResolvers;
|
||||
}
|
||||
|
||||
public void setConstructorResolvers(List<ConstructorResolver> constructorResolvers) {
|
||||
this.constructorResolvers = constructorResolvers;
|
||||
}
|
||||
|
||||
|
||||
public void addMethodResolver(MethodResolver resolver) {
|
||||
ensureMethodResolversInitialized();
|
||||
this.methodResolvers.add(this.methodResolvers.size() - 1, resolver);
|
||||
}
|
||||
|
||||
public boolean removeMethodResolver(MethodResolver methodResolver) {
|
||||
ensureMethodResolversInitialized();
|
||||
return this.methodResolvers.remove(methodResolver);
|
||||
}
|
||||
|
||||
public List<MethodResolver> getMethodResolvers() {
|
||||
ensureMethodResolversInitialized();
|
||||
return this.methodResolvers;
|
||||
}
|
||||
|
||||
public void setBeanResolver(BeanResolver beanResolver) {
|
||||
this.beanResolver = beanResolver;
|
||||
}
|
||||
|
||||
public BeanResolver getBeanResolver() {
|
||||
return this.beanResolver;
|
||||
}
|
||||
|
||||
public void setMethodResolvers(List<MethodResolver> methodResolvers) {
|
||||
this.methodResolvers = methodResolvers;
|
||||
}
|
||||
|
||||
|
||||
public void addPropertyAccessor(PropertyAccessor accessor) {
|
||||
ensurePropertyAccessorsInitialized();
|
||||
this.propertyAccessors.add(this.propertyAccessors.size() - 1, accessor);
|
||||
}
|
||||
|
||||
public boolean removePropertyAccessor(PropertyAccessor accessor) {
|
||||
return this.propertyAccessors.remove(accessor);
|
||||
}
|
||||
|
||||
public List<PropertyAccessor> getPropertyAccessors() {
|
||||
ensurePropertyAccessorsInitialized();
|
||||
return this.propertyAccessors;
|
||||
}
|
||||
|
||||
public void setPropertyAccessors(List<PropertyAccessor> propertyAccessors) {
|
||||
this.propertyAccessors = propertyAccessors;
|
||||
}
|
||||
|
||||
|
||||
public void setTypeLocator(TypeLocator typeLocator) {
|
||||
Assert.notNull(typeLocator, "TypeLocator must not be null");
|
||||
this.typeLocator = typeLocator;
|
||||
}
|
||||
|
||||
public TypeLocator getTypeLocator() {
|
||||
if (this.typeLocator == null) {
|
||||
this.typeLocator = new StandardTypeLocator();
|
||||
}
|
||||
return this.typeLocator;
|
||||
}
|
||||
|
||||
public void setTypeConverter(TypeConverter typeConverter) {
|
||||
Assert.notNull(typeConverter, "TypeConverter must not be null");
|
||||
this.typeConverter = typeConverter;
|
||||
}
|
||||
|
||||
public TypeConverter getTypeConverter() {
|
||||
if (this.typeConverter == null) {
|
||||
this.typeConverter = new StandardTypeConverter();
|
||||
}
|
||||
return this.typeConverter;
|
||||
}
|
||||
|
||||
public void setTypeComparator(TypeComparator typeComparator) {
|
||||
Assert.notNull(typeComparator, "TypeComparator must not be null");
|
||||
this.typeComparator = typeComparator;
|
||||
}
|
||||
|
||||
public TypeComparator getTypeComparator() {
|
||||
return this.typeComparator;
|
||||
}
|
||||
|
||||
public void setOperatorOverloader(OperatorOverloader operatorOverloader) {
|
||||
Assert.notNull(operatorOverloader, "OperatorOverloader must not be null");
|
||||
this.operatorOverloader = operatorOverloader;
|
||||
}
|
||||
|
||||
public OperatorOverloader getOperatorOverloader() {
|
||||
return this.operatorOverloader;
|
||||
}
|
||||
|
||||
public void setVariable(String name, Object value) {
|
||||
this.variables.put(name, value);
|
||||
}
|
||||
|
||||
public void setVariables(Map<String,Object> variables) {
|
||||
this.variables.putAll(variables);
|
||||
}
|
||||
|
||||
public void registerFunction(String name, Method method) {
|
||||
this.variables.put(name, method);
|
||||
}
|
||||
|
||||
public Object lookupVariable(String name) {
|
||||
return this.variables.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a MethodFilter which will be called during method resolution for the
|
||||
* specified type. The MethodFilter may remove methods and/or sort the methods
|
||||
* which will then be used by SpEL as the candidates to look through for a match.
|
||||
*
|
||||
* @param type the type for which the filter should be called
|
||||
* @param filter a MethodFilter, or NULL to deregister a filter for the type
|
||||
*/
|
||||
public void registerMethodFilter(Class<?> type, MethodFilter filter) {
|
||||
ensureMethodResolversInitialized();
|
||||
reflectiveMethodResolver.registerMethodFilter(type,filter);
|
||||
}
|
||||
|
||||
private void ensurePropertyAccessorsInitialized() {
|
||||
if (this.propertyAccessors == null) {
|
||||
initializePropertyAccessors();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void initializePropertyAccessors() {
|
||||
if (this.propertyAccessors == null) {
|
||||
List<PropertyAccessor> defaultAccessors = new ArrayList<PropertyAccessor>();
|
||||
defaultAccessors.add(new ReflectivePropertyAccessor());
|
||||
this.propertyAccessors = defaultAccessors;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureMethodResolversInitialized() {
|
||||
if (this.methodResolvers == null) {
|
||||
initializeMethodResolvers();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void initializeMethodResolvers() {
|
||||
if (this.methodResolvers == null) {
|
||||
List<MethodResolver> defaultResolvers = new ArrayList<MethodResolver>();
|
||||
defaultResolvers.add(reflectiveMethodResolver = new ReflectiveMethodResolver());
|
||||
this.methodResolvers = defaultResolvers;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureConstructorResolversInitialized() {
|
||||
if (this.constructorResolvers == null) {
|
||||
initializeConstructorResolvers();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void initializeConstructorResolvers() {
|
||||
if (this.constructorResolvers == null) {
|
||||
List<ConstructorResolver> defaultResolvers = new ArrayList<ConstructorResolver>();
|
||||
defaultResolvers.add(new ReflectiveConstructorResolver());
|
||||
this.constructorResolvers = defaultResolvers;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Operation;
|
||||
import org.springframework.expression.OperatorOverloader;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StandardOperatorOverloader implements OperatorOverloader {
|
||||
|
||||
public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand)
|
||||
throws EvaluationException {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException {
|
||||
throw new EvaluationException("No operation overloaded by default");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import org.springframework.expression.TypeComparator;
|
||||
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.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public class StandardTypeComparator implements TypeComparator {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public int compare(Object left, Object right) throws SpelEvaluationException {
|
||||
// If one is null, check if the other is
|
||||
if (left == null) {
|
||||
return right == null ? 0 : -1;
|
||||
} else if (right == null) {
|
||||
return 1; // left cannot be null
|
||||
}
|
||||
|
||||
// Basic number comparisons
|
||||
if (left instanceof Number && right instanceof Number) {
|
||||
Number leftNumber = (Number) left;
|
||||
Number rightNumber = (Number) right;
|
||||
if (leftNumber instanceof Double || rightNumber instanceof Double) {
|
||||
double d1 = leftNumber.doubleValue();
|
||||
double d2 = rightNumber.doubleValue();
|
||||
return Double.compare(d1,d2);
|
||||
} else 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (left instanceof Comparable) {
|
||||
return ((Comparable) left).compareTo(right);
|
||||
}
|
||||
} catch (ClassCastException cce) {
|
||||
throw new SpelEvaluationException(cce, SpelMessage.NOT_COMPARABLE, left.getClass(), right.getClass());
|
||||
}
|
||||
|
||||
throw new SpelEvaluationException(SpelMessage.NOT_COMPARABLE, left.getClass(), right.getClass());
|
||||
}
|
||||
|
||||
public boolean canCompare(Object left, Object right) {
|
||||
if (left == null || right == null) {
|
||||
return true;
|
||||
}
|
||||
if (left instanceof Number && right instanceof Number) {
|
||||
return true;
|
||||
}
|
||||
if (left instanceof Comparable) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.expression.spel.support;
|
||||
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.ConverterNotFoundException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
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}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Andy Clement
|
||||
* @since 3.0
|
||||
* @see org.springframework.core.convert.ConversionService
|
||||
*/
|
||||
public class StandardTypeConverter implements TypeConverter {
|
||||
|
||||
private static ConversionService defaultConversionService;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
|
||||
public StandardTypeConverter() {
|
||||
synchronized (this) {
|
||||
if (defaultConversionService == null) {
|
||||
defaultConversionService = new DefaultConversionService();
|
||||
}
|
||||
}
|
||||
this.conversionService = defaultConversionService;
|
||||
}
|
||||
|
||||
public StandardTypeConverter(ConversionService conversionService) {
|
||||
Assert.notNull(conversionService, "ConversionService must not be null");
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
|
||||
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.canConvert(sourceType, targetType);
|
||||
}
|
||||
|
||||
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
try {
|
||||
return this.conversionService.convert(value, sourceType, targetType);
|
||||
}
|
||||
catch (ConverterNotFoundException cenfe) {
|
||||
throw new SpelEvaluationException(cenfe, SpelMessage.TYPE_CONVERSION_ERROR, sourceType.toString(), targetType.toString());
|
||||
}
|
||||
catch (ConversionException ce) {
|
||||
throw new SpelEvaluationException(ce, SpelMessage.TYPE_CONVERSION_ERROR, sourceType.toString(), targetType.toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user