diff --git a/build-spring-framework/resources/changelog.txt b/build-spring-framework/resources/changelog.txt index fc20bd0b945..1bb477603ea 100644 --- a/build-spring-framework/resources/changelog.txt +++ b/build-spring-framework/resources/changelog.txt @@ -3,9 +3,11 @@ SPRING FRAMEWORK CHANGELOG http://www.springsource.org -Changes in version 3.0.0.M2 (2009-02-11) +Changes in version 3.0.0.M2 (2009-02-16) ---------------------------------------- +* revised expression parser API design +* added SimpleThreadScope implementation * "systemProperties" bean is not considered a default match for type Properties anymore * registered plain singletons will be fully matched according to their qualifiers * all "taskExecutor" bean properties now accept any "java.util.concurrent.Executor" @@ -28,6 +30,8 @@ Changes in version 3.0.0.M2 (2009-02-11) * Spring-created EntityManagers support JPA 2.0 draft API ("unwrap", "getQueryBuilder") * Spring initiates JPA 2.0 query timeout with remaining Spring transaction timeout * added support for WebSphere's ResourceAdapter-managed messaging transactions +* made SpringBeanAutowiringInterceptor callback signature compatible with WebSphere +* fixed JmsException/JmsUtils to fully avoid NPEs in case of cause messages being null * introduced OXM support package (originating from Spring Web Services) * introduced OXM-based MarshallingMessageConverter for JMS * introduced OXM-based MarshallingView for Spring MVC diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java index d6111964a25..35d15dc03fa 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/BeanExpressionContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.beans.factory.config; import org.springframework.beans.factory.BeanFactory; +import org.springframework.util.Assert; /** * Context object for evaluating an expression within a bean definition. @@ -32,6 +33,7 @@ public class BeanExpressionContext { public BeanExpressionContext(BeanFactory beanFactory, Scope scope) { + Assert.notNull(beanFactory, "BeanFactory must not be null"); this.beanFactory = beanFactory; this.scope = scope; } @@ -47,16 +49,37 @@ public class BeanExpressionContext { public boolean containsObject(String key) { return (this.beanFactory.containsBean(key) || - this.scope.resolveContextualObject(key) != null); + (this.scope != null && this.scope.resolveContextualObject(key) != null)); } public Object getObject(String key) { if (this.beanFactory.containsBean(key)) { return this.beanFactory.getBean(key); } - else { + else if (this.scope != null){ return this.scope.resolveContextualObject(key); } + else { + return null; + } + } + + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof BeanExpressionContext)) { + return false; + } + BeanExpressionContext otherContext = (BeanExpressionContext) other; + return (this.beanFactory == otherContext.beanFactory && this.scope == otherContext.scope); + } + + @Override + public int hashCode() { + return this.beanFactory.hashCode(); } } diff --git a/org.springframework.context/src/main/java/org/springframework/context/expression/AbstractBeanExpressionResolver.java b/org.springframework.context/src/main/java/org/springframework/context/expression/AbstractBeanExpressionResolver.java deleted file mode 100644 index 6143b2520eb..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/context/expression/AbstractBeanExpressionResolver.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.expression; - -import org.springframework.beans.factory.config.BeanExpressionContext; -import org.springframework.beans.factory.config.BeanExpressionResolver; -import org.springframework.util.Assert; - -/** - * Abstract implementation of the {@link BeanExpressionResolver} interface. - * Handles the common mixing of expression parts with literal parts. - * - *

Subclasses need to implement the {@link #evaluateExpression} template - * method for actual expression evaluation. - * - * @author Juergen Hoeller - * @since 3.0 - * @see #setExpressionPrefix - * @see #setExpressionSuffix - */ -public abstract class AbstractBeanExpressionResolver implements BeanExpressionResolver { - - /** Default expression prefix: "#{" */ - public static final String DEFAULT_EXPRESSION_PREFIX = "#{"; - - /** Default expression suffix: "}" */ - public static final String DEFAULT_EXPRESSION_SUFFIX = "}"; - - - private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX; - - private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX; - - - /** - * Set the prefix that an expression string starts with. - * The default is "#{". - * @see #DEFAULT_EXPRESSION_PREFIX - */ - public void setExpressionPrefix(String expressionPrefix) { - Assert.hasText(expressionPrefix, "Expression prefix must not be empty"); - this.expressionPrefix = expressionPrefix; - } - - /** - * Set the suffix that an expression string ends with. - * The default is "}". - * @see #DEFAULT_EXPRESSION_SUFFIX - */ - public void setExpressionSuffix(String expressionSuffix) { - Assert.hasText(expressionSuffix, "Expression suffix must not be empty"); - this.expressionSuffix = expressionSuffix; - } - - - public Object evaluate(String value, BeanExpressionContext evalContext) { - if (value == null) { - return null; - } - Object result = ""; - int prefixIndex = value.indexOf(this.expressionPrefix); - int endIndex = 0; - while (prefixIndex != -1) { - int exprStart = prefixIndex + this.expressionPrefix.length(); - int suffixIndex = value.indexOf(this.expressionSuffix, exprStart); - if (suffixIndex != -1) { - if (prefixIndex > 0) { - result = result + value.substring(endIndex, prefixIndex); - } - endIndex = suffixIndex + this.expressionSuffix.length(); - String expr = value.substring(exprStart, suffixIndex); - Object exprResult = evaluateExpression(expr, evalContext); - if (result != null && !"".equals(result)) { - result = result.toString() + exprResult.toString(); - } - else { - result = exprResult; - } - prefixIndex = value.indexOf(this.expressionPrefix, suffixIndex); - } - else { - prefixIndex = -1; - } - } - if (endIndex < value.length()) { - return result + value.substring(endIndex); - } - else { - return result; - } - } - - /** - * Evaluate the given expression. - * @param exprString the expression String to evaluate - * @param evalContext the context to evaluate the expression within - * @return the evaluation result - */ - protected abstract Object evaluateExpression(String exprString, BeanExpressionContext evalContext); - -} diff --git a/org.springframework.context/src/main/java/org/springframework/context/expression/BeanExpressionContextAccessor.java b/org.springframework.context/src/main/java/org/springframework/context/expression/BeanExpressionContextAccessor.java index 73a8a906ab9..d8b96ccfbbb 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/expression/BeanExpressionContextAccessor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/expression/BeanExpressionContextAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,19 +30,19 @@ import org.springframework.expression.PropertyAccessor; */ public class BeanExpressionContextAccessor implements PropertyAccessor { - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { - return (((BeanExpressionContext) target).containsObject(name.toString())); + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return (((BeanExpressionContext) target).containsObject(name)); } - public Object read(EvaluationContext context, Object target, Object name) throws AccessException { - return ((BeanExpressionContext) target).getObject(name.toString()); + public Object read(EvaluationContext context, Object target, String name) throws AccessException { + return ((BeanExpressionContext) target).getObject(name); } - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { return false; } - public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException { + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { throw new AccessException("Beans in a BeanFactory are read-only"); } diff --git a/org.springframework.context/src/main/java/org/springframework/context/expression/BeanFactoryAccessor.java b/org.springframework.context/src/main/java/org/springframework/context/expression/BeanFactoryAccessor.java index 9fab7e9c368..973981a8caa 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/expression/BeanFactoryAccessor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/expression/BeanFactoryAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,19 +30,19 @@ import org.springframework.expression.PropertyAccessor; */ public class BeanFactoryAccessor implements PropertyAccessor { - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { - return (((BeanFactory) target).containsBean(name.toString())); + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return (((BeanFactory) target).containsBean(name)); } - public Object read(EvaluationContext context, Object target, Object name) throws AccessException { - return ((BeanFactory) target).getBean(name.toString()); + public Object read(EvaluationContext context, Object target, String name) throws AccessException { + return ((BeanFactory) target).getBean(name); } - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { return false; } - public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException { + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { throw new AccessException("Beans in a BeanFactory are read-only"); } diff --git a/org.springframework.context/src/main/java/org/springframework/context/expression/MapAccessor.java b/org.springframework.context/src/main/java/org/springframework/context/expression/MapAccessor.java index 6b54c418eb3..4c70d0d5ac0 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/expression/MapAccessor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/expression/MapAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,19 +31,20 @@ import org.springframework.expression.PropertyAccessor; */ public class MapAccessor implements PropertyAccessor { - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { return (((Map) target).containsKey(name)); } - public Object read(EvaluationContext context, Object target, Object name) throws AccessException { + public Object read(EvaluationContext context, Object target, String name) throws AccessException { return ((Map) target).get(name); } - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { return true; } - public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException { + @SuppressWarnings("unchecked") + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { ((Map) target).put(name, newValue); } diff --git a/org.springframework.context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java b/org.springframework.context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java index 9550c9fe3ce..e880a6be4dc 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java +++ b/org.springframework.context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,18 @@ package org.springframework.context.expression; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanExpressionException; import org.springframework.beans.factory.config.BeanExpressionContext; +import org.springframework.beans.factory.config.BeanExpressionResolver; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.SpelExpressionParser; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.ParserContext; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.Assert; /** @@ -32,13 +38,61 @@ import org.springframework.util.Assert; * @author Juergen Hoeller * @since 3.0 * @see org.springframework.expression.ExpressionParser - * @see org.springframework.expression.spel.SpelExpressionParser - * @see org.springframework.expression.spel.standard.StandardEvaluationContext + * @see org.springframework.expression.spel.antlr.SpelAntlrExpressionParser + * @see org.springframework.expression.spel.support.StandardEvaluationContext */ -public class StandardBeanExpressionResolver extends AbstractBeanExpressionResolver { +public class StandardBeanExpressionResolver implements BeanExpressionResolver { - private ExpressionParser expressionParser = new SpelExpressionParser(); + /** Default expression prefix: "#{" */ + public static final String DEFAULT_EXPRESSION_PREFIX = "#{"; + /** Default expression suffix: "}" */ + public static final String DEFAULT_EXPRESSION_SUFFIX = "}"; + + + private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX; + + private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX; + + private ExpressionParser expressionParser = new SpelAntlrExpressionParser(); + + private final Map expressionCache = new ConcurrentHashMap(); + + private final Map evaluationCache = + new ConcurrentHashMap(); + + private final ParserContext beanExpressionParserContext = new ParserContext() { + public boolean isTemplate() { + return true; + } + public String getExpressionPrefix() { + return expressionPrefix; + } + public String getExpressionSuffix() { + return expressionSuffix; + } + }; + + + /** + * Set the prefix that an expression string starts with. + * The default is "#{". + * @see #DEFAULT_EXPRESSION_PREFIX + */ + public void setExpressionPrefix(String expressionPrefix) { + Assert.hasText(expressionPrefix, "Expression prefix must not be empty"); + this.expressionPrefix = expressionPrefix; + } + + /** + * Set the suffix that an expression string ends with. + * The default is "}". + * @see #DEFAULT_EXPRESSION_SUFFIX + */ + public void setExpressionSuffix(String expressionSuffix) { + Assert.hasText(expressionSuffix, "Expression suffix must not be empty"); + this.expressionSuffix = expressionSuffix; + } /** * Specify the EL parser to use for expression parsing. @@ -51,14 +105,24 @@ public class StandardBeanExpressionResolver extends AbstractBeanExpressionResolv } - protected Object evaluateExpression(String exprString, BeanExpressionContext evalContext) { + public Object evaluate(String value, BeanExpressionContext evalContext) throws BeansException { try { - Expression expr = this.expressionParser.parseExpression(exprString); - StandardEvaluationContext ec = new StandardEvaluationContext(evalContext); - ec.addPropertyAccessor(new BeanExpressionContextAccessor()); - ec.addPropertyAccessor(new BeanFactoryAccessor()); - ec.addPropertyAccessor(new MapAccessor()); - return expr.getValue(ec); + Expression expr = this.expressionCache.get(value); + if (expr == null) { + expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext); + this.expressionCache.put(value, expr); + } + StandardEvaluationContext sec = this.evaluationCache.get(evalContext); + if (sec == null) { + sec = new StandardEvaluationContext(); + sec.setRootObject(evalContext); + sec.addPropertyAccessor(new BeanExpressionContextAccessor()); + sec.addPropertyAccessor(new BeanFactoryAccessor()); + sec.addPropertyAccessor(new MapAccessor()); + customizeEvaluationContext(sec); + this.evaluationCache.put(evalContext, sec); + } + return expr.getValue(sec); } catch (Exception ex) { throw new BeanExpressionException("Expression parsing failed", ex); diff --git a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java index 6659be1bc04..6faf23bd962 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,10 @@ package org.springframework.context.expression; -import junit.framework.TestCase; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import static org.junit.Assert.*; +import org.junit.Test; import org.springframework.beans.TestBean; import org.springframework.beans.factory.ObjectFactory; @@ -25,16 +28,23 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.Scope; import org.springframework.beans.factory.support.AutowireCandidateQualifier; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.util.StopWatch; /** * @author Juergen Hoeller + * @since 3.0 */ -public class ApplicationContextExpressionTests extends TestCase { +public class ApplicationContextExpressionTests { - public void testGenericApplicationContext() { + private static final Log factoryLog = LogFactory.getLog(DefaultListableBeanFactory.class); + + @Test + public void genericApplicationContext() { GenericApplicationContext ac = new GenericApplicationContext(); AnnotationConfigUtils.registerAnnotationConfigProcessors(ac); @@ -76,7 +86,7 @@ public class ApplicationContextExpressionTests extends TestCase { GenericBeanDefinition bd2 = new GenericBeanDefinition(); bd2.setBeanClass(TestBean.class); bd2.setScope("myScope"); - bd2.getPropertyValues().addPropertyValue("name", "XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ"); + bd2.getPropertyValues().addPropertyValue("name", "{ XXX#{tb0.name}YYY#{mySpecialAttr}ZZZ }"); bd2.getPropertyValues().addPropertyValue("age", "#{mySpecialAttr}"); bd2.getPropertyValues().addPropertyValue("country", "#{systemProperties.country}"); ac.registerBeanDefinition("tb2", bd2); @@ -107,7 +117,7 @@ public class ApplicationContextExpressionTests extends TestCase { assertEquals(42, tb1.getAge()); TestBean tb2 = ac.getBean("tb2", TestBean.class); - assertEquals("XXXmyNameYYY42ZZZ", tb2.getName()); + assertEquals("{ XXXmyNameYYY42ZZZ }", tb2.getName()); assertEquals(42, tb2.getAge()); assertEquals("UK", tb2.getCountry()); @@ -134,6 +144,37 @@ public class ApplicationContextExpressionTests extends TestCase { } } + @Test + public void prototypeCreationIsFastEnough() { + if (factoryLog.isTraceEnabled() || factoryLog.isDebugEnabled()) { + // Skip this test: Trace logging blows the time limit. + return; + } + GenericApplicationContext ac = new GenericApplicationContext(); + RootBeanDefinition rbd = new RootBeanDefinition(TestBean.class); + rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + rbd.getPropertyValues().addPropertyValue("name", "juergen"); + rbd.getPropertyValues().addPropertyValue("country", "#{systemProperties.country}"); + ac.registerBeanDefinition("test", rbd); + ac.refresh(); + StopWatch sw = new StopWatch(); + sw.start("prototype"); + System.getProperties().put("country", "UK"); + try { + for (int i = 0; i < 100000; i++) { + TestBean tb = (TestBean) ac.getBean("test"); + assertEquals("juergen", tb.getName()); + assertEquals("UK", tb.getCountry()); + } + sw.stop(); + } + finally { + System.getProperties().remove("country"); + } + System.out.println(sw.getTotalTimeMillis()); + assertTrue("Prototype creation took too long: " + sw.getTotalTimeMillis(), sw.getTotalTimeMillis() < 6000); + } + public static class ValueTestBean { diff --git a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java index 2f789c89a8d..9c31ca503f9 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,6 +73,12 @@ public abstract class ClassUtils { */ private static final Map primitiveWrapperTypeMap = new HashMap(8); + /** + * Map with primitive type as key and corresponding wrapper + * type as value, for example: int.class -> Integer.class. + */ + private static final Map primitiveTypeToWrapperMap = new HashMap(8); + /** * Map with primitive type name as key and corresponding primitive * type as value, for example: "int" -> "int.class". @@ -90,6 +96,10 @@ public abstract class ClassUtils { primitiveWrapperTypeMap.put(Long.class, long.class); primitiveWrapperTypeMap.put(Short.class, short.class); + for (Map.Entry entry : primitiveWrapperTypeMap.entrySet()) { + primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey()); + } + Set primitiveTypes = new HashSet(16); primitiveTypes.addAll(primitiveWrapperTypeMap.values()); primitiveTypes.addAll(Arrays.asList( @@ -701,6 +711,17 @@ public abstract class ClassUtils { return (clazz.isArray() && isPrimitiveWrapper(clazz.getComponentType())); } + /** + * Resolve the given class if it is a primitive class, + * returning the corresponding primitive wrapper type instead. + * @param clazz the class to check + * @return the original class, or a primitive wrapper for the original primitive type + */ + public static Class resolvePrimitiveIfNecessary(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); + return (clazz.isPrimitive() ? primitiveTypeToWrapperMap.get(clazz) : clazz); + } + /** * Check if the right-hand side type may be assigned to the left-hand side * type, assuming setting by reflection. Considers primitive wrapper diff --git a/org.springframework.expression/expression.iml b/org.springframework.expression/expression.iml index cdeaa53b05b..9c786be5078 100644 --- a/org.springframework.expression/expression.iml +++ b/org.springframework.expression/expression.iml @@ -10,6 +10,7 @@ + diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/AccessException.java b/org.springframework.expression/src/main/java/org/springframework/expression/AccessException.java index 2a2e4d014a2..21b4c3bc6c5 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/AccessException.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/AccessException.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** * An AccessException is thrown by an accessor if it has an unexpected problem. * * @author Andy Clement + * @since 3.0 */ public class AccessException extends Exception { /** * Create an AccessException with a specific message and cause. - * * @param message the message * @param cause the cause */ @@ -34,7 +35,6 @@ public class AccessException extends Exception { /** * Create an AccessException with a specific message. - * * @param message the message */ public AccessException(String message) { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/CacheablePropertyAccessor.java b/org.springframework.expression/src/main/java/org/springframework/expression/CacheablePropertyAccessor.java deleted file mode 100644 index 7567da41bba..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/CacheablePropertyAccessor.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.springframework.expression; - -/** - * A CacheablePropertyAccessor is an optimized PropertyAccessor where the two parts of accessing the property are - * separated: (1) resolving the property and (2) retrieving its value. In some cases there is a large cost to - * discovering which property an expression refers to and once discovered it will always resolve to the same property. - * In these situations a CacheablePropertyAccessor enables the resolution to be done once and a reusable object (an - * executor) returned that can be called over and over to retrieve the property value without going through resolution - * again. - *

- * - * @author Andy Clement - */ -public abstract class CacheablePropertyAccessor implements PropertyAccessor { - - /** - * Attempt to resolve the named property and return an executor that can be called to get the value of that - * property. Return null if the property cannot be resolved. - * - * @param context the evaluation context - * @param target the target upon which the property is being accessed - * @param name the name of the property being accessed - * @return a reusable executor that can retrieve the property value - */ - public abstract PropertyReaderExecutor getReaderAccessor(EvaluationContext context, Object target, Object name); - - /** - * Attempt to resolve the named property and return an executor that can be called to set the value of that - * property. Return null if the property cannot be resolved. - * - * @param context the evaluation context - * @param target the target upon which the property is being accessed - * @param name the name of the property to be set - * @return a reusable executor that can set the property value - */ - public abstract PropertyWriterExecutor getWriterAccessor(EvaluationContext context, Object target, Object name); - - // Implementation of PropertyAccessor follows, based on the resolver/executor model - - public final boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { - return getReaderAccessor(context, target, name) != null; - } - - public final boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { - return getWriterAccessor(context, target, name) != null; - } - - public final Object read(EvaluationContext context, Object target, Object name) throws AccessException { - return getReaderAccessor(context, target, name).execute(context, target); - } - - public final void write(EvaluationContext context, Object target, Object name, Object newValue) - throws AccessException { - getWriterAccessor(context, target, name).execute(context, target, newValue); - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorExecutor.java index b800e6bce93..091d5bd9dc4 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorExecutor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; // TODO Is the resolver/executor model too pervasive in this package? @@ -26,12 +27,12 @@ package org.springframework.expression; * back to the resolvers to ask for a new one. * * @author Andy Clement + * @since 3.0 */ public interface ConstructorExecutor { /** * Execute a constructor in the specified context using the specified arguments. - * * @param context the evaluation context in which the command is being executed * @param arguments the arguments to the constructor call, should match (in terms of number and type) whatever the * command will need to run diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorResolver.java index 87037c64c55..922875464a6 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorResolver.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/ConstructorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** @@ -20,6 +21,7 @@ package org.springframework.expression; * that constructor. The ConstructorExecutor will be cached but if it 'goes stale' the resolvers will be called again. * * @author Andy Clement + * @since 3.0 */ public interface ConstructorResolver { @@ -27,13 +29,12 @@ public interface ConstructorResolver { * Within the supplied context determine a suitable constructor on the supplied type that can handle the specified * arguments. Return a ConstructorExecutor that can be used to invoke that constructor (or null if no constructor * could be found). - * * @param context the current evaluation context - * @param typename the type upon which to look for the constructor + * @param typeName the type upon which to look for the constructor * @param argumentTypes the arguments that the constructor must be able to handle * @return a ConstructorExecutor that can invoke the constructor, or null if non found */ - ConstructorExecutor resolve(EvaluationContext context, String typename, Class[] argumentTypes) + ConstructorExecutor resolve(EvaluationContext context, String typeName, Class[] argumentTypes) throws AccessException; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationContext.java index 9fe329b5c6c..33ac50df67d 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationContext.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,61 +13,53 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; import java.util.List; -import org.springframework.expression.spel.standard.StandardEvaluationContext; -import org.springframework.expression.spel.standard.StandardTypeUtilities; - /** - * Expressions are executed in an evaluation context. It is in this context that references are resolved when - * encountered during expression evaluation. + * Expressions are executed in an evaluation context. It is in this context that references + * are resolved when encountered during expression evaluation. * - * There is a default implementation of the EvaluationContext, {@link StandardEvaluationContext} that can be extended, - * rather than having to implement everything. + * There is a default implementation of the EvaluationContext, + * {@link org.springframework.expression.spel.support.StandardEvaluationContext} + * that can be extended, rather than having to implement everything. * * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 */ public interface EvaluationContext { /** * @return the root context object against which unqualified properties/methods/etc should be resolved */ - Object getRootContextObject(); - - /** - * @return a TypeUtilities implementation that can be used for looking up types, converting types, comparing types, - * and overloading basic operators for types. A standard implementation is provided in {@link StandardTypeUtilities} - */ - TypeUtils getTypeUtils(); - - /** - * Look up a named variable within this execution context. - * - * @param name variable to lookup - * @return the value of the variable - */ - Object lookupVariable(String name); + Object getRootObject(); /** * Set a named variable within this execution context to a specified value. - * * @param name variable to set * @param value value to be placed in the variable */ void setVariable(String name, Object value); + /** + * Look up a named variable within this execution context. + * @param name variable to lookup + * @return the value of the variable + */ + Object lookupVariable(String name); + // TODO lookupReference() - is it too expensive to return all objects within a context? /** * Look up an object reference in a particular context. If no contextName is specified (null), assume the default - * context. If no objectName is specified (null), return all objects in the specified context (List). - * - * @param contextName the context in which to perform the lookup (or null for default context) - * @param objectName the object to lookup in the context (or null to get all objects) - * @return a specific object or List + * context. If no objectName is specified (null), return all objects in the specified context (List). + * @param contextName the context in which to perform the lookup (or null for default context) + * @param objectName the object to lookup in the context (or null to get all objects) + * @return a specific object or List */ - Object lookupReference(Object contextName, Object objectName) throws EvaluationException; + Object lookupReference(Object contextName, String objectName) throws EvaluationException; /** * @return a list of resolvers that will be asked in turn to locate a constructor @@ -84,4 +76,25 @@ public interface EvaluationContext { */ List getPropertyAccessors(); + /** + * @return a type locator that can be used to find types, either by short or fully qualified name. + */ + TypeLocator getTypeLocator(); + + /** + * @return a type comparator for comparing pairs of objects for equality. + */ + TypeComparator getTypeComparator(); + + /** + * @return a type converter that can convert (or coerce) a value from one type to another. + */ + TypeConverter getTypeConverter(); + + /** + * @return an operator overloader that may support mathematical operations between more than the standard set of + * types + */ + OperatorOverloader getOperatorOverloader(); + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationException.java b/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationException.java index 4bbf094d2fd..589d6cd1e10 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationException.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/EvaluationException.java @@ -1,36 +1,34 @@ /* - * Copyright 2004-2008 the original author or authors. - * + * Copyright 2002-2009 the original author or authors. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** * Base class for exceptions occurring during expression parsing and evaluation. * * @author Andy Clement + * @since 3.0 */ public class EvaluationException extends Exception { - /** - * The expression string. - */ private String expressionString; + /** - * Creates a new expression exception. The expressionString field should be set by a later call to - * setExpressionString(). - * + * Creates a new expression exception. * @param cause the underlying cause of this exception */ public EvaluationException(Throwable cause) { @@ -39,7 +37,6 @@ public class EvaluationException extends Exception { /** * Creates a new expression parsing exception. - * * @param expressionString the expression string that could not be parsed * @param cause the underlying cause of this exception */ @@ -49,7 +46,6 @@ public class EvaluationException extends Exception { /** * Creates a new expression exception. - * * @param expressionString the expression string * @param message a descriptive message * @param cause the underlying cause of this exception @@ -61,7 +57,6 @@ public class EvaluationException extends Exception { /** * Creates a new expression exception. - * * @param expressionString the expression string * @param message a descriptive message */ @@ -73,27 +68,15 @@ public class EvaluationException extends Exception { /** * Creates a new expression exception. The expressionString field should be set by a later call to * setExpressionString(). - * * @param message a descriptive message */ public EvaluationException(String message) { super(message); } - /** - * Set the expression string, called on exceptions where the expressionString is not known at the time of exception - * creation. - * - * @param expressionString the expression string - */ - protected final void setExpressionString(String expressionString) { - this.expressionString = expressionString; + + public final String getExpressionString() { + return this.expressionString; } - /** - * @return the expression string - */ - public final String getExpressionString() { - return expressionString; - } } \ No newline at end of file diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/Expression.java b/org.springframework.expression/src/main/java/org/springframework/expression/Expression.java index 45c8daacea9..d92238bd61f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/Expression.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/Expression.java @@ -1,18 +1,19 @@ /* - * Copyright 2004-2008 the original author or authors. - * + * Copyright 2002-2009 the original author or authors. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** @@ -22,6 +23,7 @@ package org.springframework.expression; * * @author Keith Donald * @author Andy Clement + * @since 3.0 */ public interface Expression { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/ExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/ExpressionParser.java index 2b6cfc66954..913749a0f99 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/ExpressionParser.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/ExpressionParser.java @@ -1,56 +1,56 @@ /* - * Copyright 2004-2008 the original author or authors. - * + * Copyright 2002-2009 the original author or authors. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** - * Parses expression strings into compiled expressions that can be evaluated. Supports parsing templates as well as - * standard expression strings. - * + * Parses expression strings into compiled expressions that can be evaluated. + * Supports parsing templates as well as standard expression strings. + * * @author Keith Donald * @author Andy Clement + * @since 3.0 */ public interface ExpressionParser { /** - * Parse the expression string and return an Expression object you can use for repeated evaluation. Some examples: - * + * Parse the expression string and return an Expression object you can use for repeated evaluation. + *

Some examples: + *

+	 *     3 + 4
+	 *     name.firstName
+	 * 
+ * @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. + *

Some examples: *

 	 *     3 + 4
 	 *     name.firstName
 	 * 
- * * @param expressionString the raw expression string to parse * @param context a context for influencing this expression parsing routine (optional) * @return an evaluator for the parsed expression * @throws ParseException an exception occurred during parsing */ - public Expression parseExpression(String expressionString, ParserContext context) throws ParseException; + Expression parseExpression(String expressionString, ParserContext context) throws ParseException; - /** - * Parse the expression string and return an Expression object you can use for repeated evaluation. Some examples: - * - *
-	 *     3 + 4
-	 *     name.firstName
-	 * 
- * - * @param expressionString the raw expression string to parse - * @return an evaluator for the parsed expression - * @throws ParseException an exception occurred during parsing - */ - public Expression parseExpression(String expressionString) throws ParseException; - -} \ No newline at end of file +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/MethodExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/MethodExecutor.java index 99bf35aabc6..55175a3aede 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/MethodExecutor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/MethodExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** @@ -20,24 +21,24 @@ package org.springframework.expression; * without going back to the resolvers. For example, the particular method to run on an object may be discovered by the * reflection method resolver - it will then build a MethodExecutor that executes that method and the MethodExecutor can * be reused without needing to go back to the resolver to discover the method again. - *

- * They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go + * + *

They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go * back to the resolvers to ask for a new one. - * + * * @author Andy Clement + * @since 3.0 */ public interface MethodExecutor { /** * Execute a command using the specified arguments, and using the specified expression state. - * * @param context the evaluation context in which the command is being executed * @param target the target object of the call - null for static methods - * @param methodArguments the arguments to the executor, should match (in terms of number and type) whatever the + * @param arguments the arguments to the executor, should match (in terms of number and type) whatever the * command will need to run * @return the value returned from execution * @throws AccessException if there is a problem executing the command or the MethodExecutor is no longer valid */ - Object execute(EvaluationContext context, Object target, Object... methodArguments) throws AccessException; + Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/MethodResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/MethodResolver.java index e766214eafb..18824397b7e 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/MethodResolver.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/MethodResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** @@ -20,6 +21,7 @@ package org.springframework.expression; * command executor will be cached but if it 'goes stale' the resolvers will be called again. * * @author Andy Clement + * @since 3.0 */ public interface MethodResolver { @@ -27,7 +29,6 @@ public interface MethodResolver { * Within the supplied context determine a suitable method on the supplied object that can handle the specified * arguments. Return a MethodExecutor that can be used to invoke that method (or null if no method * could be found). - * * @param context the current evaluation context * @param targetObject the object upon which the method is being called * @param argumentTypes the arguments that the constructor must be able to handle diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java b/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java index 4fd211bd67c..b0ff0dfe015 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/Operation.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression; +package org.springframework.expression; /** * Supported operations that an {@link OperatorOverloader} can implement for any pair of operands. * * @author Andy Clement + * @since 3.0 */ public enum Operation { - ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS; + + ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/OperatorOverloader.java b/org.springframework.expression/src/main/java/org/springframework/expression/OperatorOverloader.java index 2d40bc26684..788638ac9e7 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/OperatorOverloader.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/OperatorOverloader.java @@ -1,3 +1,19 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.expression; /** @@ -5,6 +21,7 @@ package org.springframework.expression; * implementation of OperatorOverloader, a user of the expression language can support these operations on other types. * * @author Andy Clement + * @since 3.0 */ public interface OperatorOverloader { @@ -12,26 +29,27 @@ public interface OperatorOverloader { // TODO Operator overloading needs some testing! /** - * Return true if the operator overloader supports the specified operation between the two operands and so should be - * invoked to handle it. - * + * Return true if the operator overloader supports the specified operation + * between the two operands and so should be invoked to handle it. * @param operation the operation to be performed * @param leftOperand the left operand * @param rightOperand the right operand * @return true if the OperatorOverloader supports the specified operation between the two operands * @throws EvaluationException if there is a problem performing the operation */ - boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException; + boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) + throws EvaluationException; /** - * Execute the specified operation on two operands, returning a result. See {@link Operation} for supported - * operations. - * + * Execute the specified operation on two operands, returning a result. + * See {@link Operation} for supported operations. * @param operation the operation to be performed * @param leftOperand the left operand * @param rightOperand the right operand * @return the result of performing the operation on the two operands * @throws EvaluationException if there is a problem performing the operation */ - Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException; + Object operate(Operation operation, Object leftOperand, Object rightOperand) + throws EvaluationException; + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/ParseException.java b/org.springframework.expression/src/main/java/org/springframework/expression/ParseException.java index 2502d9bfcd1..615d01a60e5 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/ParseException.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/ParseException.java @@ -1,36 +1,34 @@ /* - * Copyright 2004-2008 the original author or authors. - * + * Copyright 2002-2009 the original author or authors. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** * Base class for exceptions occurring during expression parsing and evaluation. - * + * * @author Andy Clement + * @since 3.0 */ public class ParseException extends Exception { - /** - * The expression string. - */ private String expressionString; + /** - * Creates a new expression exception. The expressionString field should be set by a later call to - * setExpressionString(). - * + * Creates a new expression exception. * @param cause the underlying cause of this exception */ public ParseException(Throwable cause) { @@ -39,7 +37,6 @@ public class ParseException extends Exception { /** * Creates a new expression parsing exception. - * * @param expressionString the expression string that could not be parsed * @param cause the underlying cause of this exception */ @@ -49,7 +46,6 @@ public class ParseException extends Exception { /** * Creates a new expression exception. - * * @param expressionString the expression string * @param message a descriptive message * @param cause the underlying cause of this exception @@ -61,7 +57,6 @@ public class ParseException extends Exception { /** * Creates a new expression exception. - * * @param expressionString the expression string * @param message a descriptive message */ @@ -70,20 +65,9 @@ public class ParseException extends Exception { this.expressionString = expressionString; } - /** - * Set the expression string, called on exceptions where the expressionString is not known at the time of exception - * creation. - * - * @param expressionString the expression string - */ - protected final void setExpressionString(String expressionString) { - this.expressionString = expressionString; + + public final String getExpressionString() { + return this.expressionString; } - /** - * @return the expression string - */ - public final String getExpressionString() { - return expressionString; - } -} \ No newline at end of file +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/ParserContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/ParserContext.java index 393dffc90c0..1ef0a5ca4b6 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/ParserContext.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/ParserContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2004-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** * Input provided to an expression parser that can influence an expression parsing/compilation routine. - * + * * @author Keith Donald * @author Andy Clement + * @since 3.0 */ public interface ParserContext { @@ -35,21 +37,22 @@ public interface ParserContext { * * @return true if the expression is a template, false otherwise */ - public boolean isTemplate(); + boolean isTemplate(); /** * For template expressions, returns the prefix that identifies the start of an expression block within a string. - * For example "${" - * + * For example: "${" + * * @return the prefix that identifies the start of an expression */ - public String getExpressionPrefix(); + String getExpressionPrefix(); /** - * For template expressions, return the prefix that identifies the end of an expression block within a string. For - * example "}$" - * + * For template expressions, return the prefix that identifies the end of an expression block within a string. + * For example: "}" + * * @return the suffix that identifies the end of an expression */ - public String getExpressionSuffix(); -} \ No newline at end of file + String getExpressionSuffix(); + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/PropertyAccessor.java b/org.springframework.expression/src/main/java/org/springframework/expression/PropertyAccessor.java index c9924b8369d..04a4a32e446 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/PropertyAccessor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/PropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** @@ -23,66 +24,62 @@ package org.springframework.expression; * to determine if it can read or write them. Property resolvers are considered to be ordered and each will be called in * turn. The only rule that affects the call order is that any naming the target class directly in * getSpecifiedTargetClasses() will be called first, before the general resolvers. - *

- * If the cost of locating the property is expensive, in relation to actually retrieving its value, consider extending + * + *

If the cost of locating the property is expensive, in relation to actually retrieving its value, consider extending * CacheablePropertyAccessor rather than directly implementing PropertyAccessor. A CacheablePropertyAccessor enables the * discovery (resolution) of the property to be done once and then an object (an executor) returned and cached by the * infrastructure that can be used repeatedly to retrieve the property value. * * @author Andy Clement + * @since 3.0 */ public interface PropertyAccessor { /** * Return an array of classes for which this resolver should be called. Returning null indicates this is a general * resolver that can be called in an attempt to resolve a property on any type. - * * @return an array of classes that this resolver is suitable for (or null if a general resolver) */ - public Class[] getSpecificTargetClasses(); + Class[] getSpecificTargetClasses(); /** * Called to determine if a resolver instance is able to access a specified property on a specified target object. - * * @param context the evaluation context in which the access is being attempted * @param target the target object upon which the property is being accessed * @param name the name of the property being accessed * @return true if this resolver is able to read the property * @throws AccessException if there is any problem determining whether the property can be read */ - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException; + boolean canRead(EvaluationContext context, Object target, String name) throws AccessException; /** * Called to read a property from a specified target object - * * @param context the evaluation context in which the access is being attempted * @param target the target object upon which the property is being accessed * @param name the name of the property being accessed * @return Object the value of the property * @throws AccessException if there is any problem accessing the property value */ - public Object read(EvaluationContext context, Object target, Object name) throws AccessException; + Object read(EvaluationContext context, Object target, String name) throws AccessException; /** * Called to determine if a resolver instance is able to write to a specified property on a specified target object. - * * @param context the evaluation context in which the access is being attempted * @param target the target object upon which the property is being accessed * @param name the name of the property being accessed * @return true if this resolver is able to write to the property * @throws AccessException if there is any problem determining whether the property can be written to */ - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException; + boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException; /** * Called to write to a property on a specified target object. Should only succeed if canWrite() also returns true. - * * @param context the evaluation context in which the access is being attempted * @param target the target object upon which the property is being accessed * @param name the name of the property being accessed * @param newValue the new value for the property * @throws AccessException if there is any problem writing to the property value */ - public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException; + void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/PropertyReaderExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/PropertyReaderExecutor.java deleted file mode 100644 index d29f0ad9d19..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/PropertyReaderExecutor.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression; - -/** - * If a property accessor is built upon the CacheablePropertyAccessor class then once the property - * has been resolved the accessor will return an instance of this PropertyReaderExecutor interface - * that can be cached and repeatedly called to access the value of the property. - *

- * They can become stale, and in that case should throw an AccessException - this will cause the - * infrastructure to go back to the resolvers to ask for a new one. - * - * @author Andy Clement - */ -public interface PropertyReaderExecutor { - - /** - * Return the value of a property for the specified target. - * - * @param context the evaluation context in which the command is being executed - * @param targetObject the target object on which property access is being attempted - * @return the property value - * @throws AccessException if there is a problem accessing the property or this executor has become stale - */ - Object execute(EvaluationContext context, Object targetObject) throws AccessException; - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/PropertyWriterExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/PropertyWriterExecutor.java deleted file mode 100644 index 164c7e6b4e3..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/PropertyWriterExecutor.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.expression; - -/** - * If a property accessor is built upon the CacheablePropertyAccessor class then once the property - * has been resolved the accessor will return an instance of this PropertyWriterExecutor interface - * that can be cached and repeatedly called to set the value of the property. - * - *

They can become stale, and in that case should throw an AccessException - this will cause the - * infrastructure to go back to the resolvers to ask for a new one. - * - * @author Andy Clement - */ -public interface PropertyWriterExecutor { - - /** - * Set the value of a property to the supplied new value. - * @param context the evaluation context in which the command is being executed - * @param targetObject the target object on which property write is being attempted - * @param newValue the new value for the property - * @throws AccessException if there is a problem setting the property or this executor has become stale - */ - void execute(EvaluationContext context, Object targetObject, Object newValue) throws AccessException; - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/TypeComparator.java b/org.springframework.expression/src/main/java/org/springframework/expression/TypeComparator.java index ef4329bf69d..117ac772ab1 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/TypeComparator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/TypeComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; /** @@ -20,12 +21,12 @@ package org.springframework.expression; * return value is the same as for {@link Comparable}. * * @author Andy Clement + * @since 3.0 */ public interface TypeComparator { /** * Compare two objects. - * * @param firstObject the first object * @param secondObject the second object * @return 0 if they are equal, <0 if the first is smaller than the second, or >0 if the first is larger than the @@ -36,11 +37,10 @@ public interface TypeComparator { /** * Return true if the comparator can compare these two objects - * * @param firstObject the first object * @param secondObject the second object * @return true if the comparator can compare these objects */ - public boolean canCompare(Object firstObject, Object secondObject); + boolean canCompare(Object firstObject, Object secondObject); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/TypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/TypeConverter.java index 11942447ee1..b1f896015e1 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/TypeConverter.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/TypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression; -import org.springframework.expression.spel.standard.StandardIndividualTypeConverter; -import org.springframework.expression.spel.standard.StandardTypeConverter; - /** - * A type converter can convert values between different types. There is a default implementation called - * {@link StandardTypeConverter} that supports some basic conversions. That default implementation can be extended - * through subclassing or via registration of new {@link StandardIndividualTypeConverter} instances with the - * StandardTypeConverter. - * + * A type converter can convert values between different types encountered + * during expression evaluation. + * * @author Andy Clement + * @since 3.0 */ public interface TypeConverter { // TODO replace this stuff with Keiths spring-binding conversion code @@ -32,21 +29,19 @@ public interface TypeConverter { /** * Convert (may coerce) a value from one type to another, for example from a boolean to a string. - * * @param value the value to be converted * @param targetType the type that the value should be converted to if possible * @return the converted value * @throws EvaluationException if conversion is not possible */ - Object convertValue(Object value, Class targetType) throws EvaluationException; + T convertValue(Object value, Class targetType) throws EvaluationException; /** * Return true if the type converter can convert the specified type to the desired target type. - * * @param sourceType the type to be converted from * @param targetType the type to be converted to * @return true if that conversion can be performed */ - public boolean canConvert(Class sourceType, Class targetType); + boolean canConvert(Class sourceType, Class targetType); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/TypeLocator.java b/org.springframework.expression/src/main/java/org/springframework/expression/TypeLocator.java index 0306adfdd1c..4a22a82c199 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/TypeLocator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/TypeLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,13 @@ package org.springframework.expression; -import org.springframework.expression.spel.standard.StandardTypeLocator; - /** - * Implementors of this interface are expected to be able to locate types. They may use custom classloaders or the - * and deal with common package prefixes (java.lang, etc) however they wish. See - * {@link StandardTypeLocator} for an example implementation. + * Implementors of this interface are expected to be able to locate types. They may use custom classloaders + * or the and deal with common package prefixes (java.lang, etc) however they wish. See + * {@link org.springframework.expression.spel.support.StandardTypeLocator} for an example implementation. * * @author Andy Clement + * @since 3.0 */ public interface TypeLocator { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/TypeUtils.java b/org.springframework.expression/src/main/java/org/springframework/expression/TypeUtils.java deleted file mode 100644 index 13d55d5a07a..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/TypeUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression; - -/** - * TypeUtilities brings together the various kinds of type related function that may occur - * whilst working with expressions. An implementor is providing support for four type related - * facilities: - *

    - *
  • a mechanism for finding types - *
  • a mechanism for comparing types - *
  • a mechanism for type conversion/coercion - *
  • a mechanism for overloading mathematical operations (add/subtract/etc) - *
- * - * @author Andy Clement - */ -public interface TypeUtils { - - /** - * @return a type locator that can be used to find types, either by short or fully qualified name. - */ - TypeLocator getTypeLocator(); - - /** - * @return a type comparator for comparing pairs of objects for equality. - */ - TypeComparator getTypeComparator(); - - /** - * @return a type converter that can convert (or coerce) a value from one type to another. - */ - TypeConverter getTypeConverter(); - - /** - * @return an operator overloader that may support mathematical operations between more than the standard set of - * types - */ - OperatorOverloader getOperatorOverloader(); - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java index b55d67608d4..a86d0592853 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java @@ -1,78 +1,99 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.expression.common; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; +import org.springframework.util.ObjectUtils; /** * Represents a template expression broken into pieces. Each piece will be an Expression but pure text parts to the - * template will be represented as LiteralExpression objects. An example of a template expression might be:
- * "Hello ${getName()}"
- * 
which will be represented as a CompositeStringExpression of two parts. The first part being a - * LiteralExpression representing 'Hello ' and the second part being a real expression that will call getName() when - * invoked. + * template will be represented as LiteralExpression objects. An example of a template expression might be: + * + *
+ * "Hello ${getName()}"
+ * + * which will be represented as a CompositeStringExpression of two parts. The first part being a + * LiteralExpression representing 'Hello ' and the second part being a real expression that will + * call getName() when invoked. * * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 */ public class CompositeStringExpression implements Expression { private final String expressionString; - /** - * The array of expressions that make up the composite expression - */ + /** The array of expressions that make up the composite expression */ private final Expression[] expressions; + public CompositeStringExpression(String expressionString, Expression[] expressions) { this.expressionString = expressionString; this.expressions = expressions; } - public String getExpressionString() { - return expressionString; + + public final String getExpressionString() { + return this.expressionString; } public String getValue() throws EvaluationException { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < expressions.length; i++) { - // TODO is stringify ok for the non literal components? or should the converters be used? see another - // case below - sb.append(expressions[i].getValue()); + for (Expression expression : this.expressions) { + sb.append(ObjectUtils.getDisplayString(expression.getValue())); } return sb.toString(); } public String getValue(EvaluationContext context) throws EvaluationException { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < expressions.length; i++) { - sb.append(expressions[i].getValue(context)); + for (Expression expression : this.expressions) { + sb.append(ObjectUtils.getDisplayString(expression.getValue(context))); } return sb.toString(); } - public Class getValueType(EvaluationContext context) throws EvaluationException { + public Class getValueType(EvaluationContext context) { return String.class; } - public Class getValueType() throws EvaluationException { + public Class getValueType() { return String.class; } public void setValue(EvaluationContext context, Object value) throws EvaluationException { - throw new EvaluationException(expressionString, "Cannot call setValue() on a composite expression"); + throw new EvaluationException(this.expressionString, "Cannot call setValue on a composite expression"); } public T getValue(EvaluationContext context, Class expectedResultType) throws EvaluationException { Object value = getValue(context); - return (T)ExpressionUtils.convert(context, value, expectedResultType); + return ExpressionUtils.convert(context, value, expectedResultType); } public T getValue(Class expectedResultType) throws EvaluationException { Object value = getValue(); - return (T)ExpressionUtils.convert(null, value, expectedResultType); + return ExpressionUtils.convert(null, value, expectedResultType); } - public boolean isWritable(EvaluationContext context) throws EvaluationException { + public boolean isWritable(EvaluationContext context) { return false; } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/DefaultNonTemplateParserContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/DefaultNonTemplateParserContext.java deleted file mode 100644 index 62c0d4f45d3..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/common/DefaultNonTemplateParserContext.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.springframework.expression.common; - -import org.springframework.expression.ParserContext; - -public class DefaultNonTemplateParserContext implements ParserContext { - - public static final DefaultNonTemplateParserContext INSTANCE = new DefaultNonTemplateParserContext(); - - private DefaultNonTemplateParserContext() { - } - - public String getExpressionPrefix() { - return null; - } - - public String getExpressionSuffix() { - return null; - } - - public boolean isTemplate() { - return false; - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/DefaultTemplateParserContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/DefaultTemplateParserContext.java deleted file mode 100644 index c459929048e..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/common/DefaultTemplateParserContext.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.springframework.expression.common; - -import org.springframework.expression.ParserContext; - -public class DefaultTemplateParserContext implements ParserContext { - - public static final DefaultTemplateParserContext INSTANCE = new DefaultTemplateParserContext(); - - private DefaultTemplateParserContext() { - } - - public String getExpressionPrefix() { - return "${"; - } - - public String getExpressionSuffix() { - return "}"; - } - - public boolean isTemplate() { - return true; - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java index 319df42963c..f3b8084cd42 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,43 +13,43 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.common; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; -import org.springframework.expression.TypeConverter; -import org.springframework.expression.TypeUtils; +import org.springframework.util.ClassUtils; /** * Common utility functions that may be used by any Expression Language provider. - * + * * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 */ -public class ExpressionUtils { +public abstract class ExpressionUtils { /** * Determines if there is a type converter available in the specified context and attempts to use it to convert the * supplied value to the specified type. Throws an exception if conversion is not possible. - * * @param context the evaluation context that may define a type converter * @param value the value to convert (may be null) - * @param toType the type to attempt conversion to + * @param targetType the type to attempt conversion to * @return the converted value * @throws EvaluationException if there is a problem during conversion or conversion of the value to the specified * type is not supported */ - public static Object convert(EvaluationContext context, Object value, Class toType) throws EvaluationException { - if (value == null || toType == null || toType.isAssignableFrom(value.getClass())) { - return value; + @SuppressWarnings("unchecked") + public static T convert(EvaluationContext context, Object value, Class targetType) + throws EvaluationException { + + if (targetType == null || ClassUtils.isAssignableValue(targetType, value)) { + return (T) value; } if (context != null) { - TypeUtils typeUtils = context.getTypeUtils(); - if (typeUtils != null) { - TypeConverter typeConverter = typeUtils.getTypeConverter(); - return typeConverter.convertValue(value, toType); - } + return context.getTypeConverter().convertValue(value, targetType); } - throw new EvaluationException("Cannot convert value '" + value + "' to type '" + toType.getName() + "'"); + throw new EvaluationException("Cannot convert value '" + value + "' to type '" + targetType.getName() + "'"); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/LiteralExpression.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/LiteralExpression.java index 8abb0af12c2..815b03fb96d 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/common/LiteralExpression.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/common/LiteralExpression.java @@ -1,3 +1,19 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.expression.common; import org.springframework.expression.EvaluationContext; @@ -8,35 +24,34 @@ import org.springframework.expression.Expression; * A very simple hardcoded implementation of the Expression interface that represents a string literal. It is used with * CompositeStringExpression when representing a template expression which is made up of pieces - some being real * expressions to be handled by an EL implementation like Spel, and some being just textual elements. - * + * * @author Andy Clement - * + * @since 3.0 */ public class LiteralExpression implements Expression { - /** - * Fixed literal value of this expression - */ + /** Fixed literal value of this expression */ private final String literalValue; + public LiteralExpression(String literalValue) { this.literalValue = literalValue; } - public String getExpressionString() { - return literalValue; - // return new StringBuilder().append("'").append(literalValue).append("'").toString(); + + public final String getExpressionString() { + return this.literalValue; } - public String getValue() throws EvaluationException { - return literalValue; + public String getValue() { + return this.literalValue; } - public String getValue(EvaluationContext context) throws EvaluationException { - return literalValue; + public String getValue(EvaluationContext context) { + return this.literalValue; } - public Class getValueType(EvaluationContext context) throws EvaluationException { + public Class getValueType(EvaluationContext context) { return String.class; } @@ -46,19 +61,19 @@ public class LiteralExpression implements Expression { public T getValue(EvaluationContext context, Class expectedResultType) throws EvaluationException { Object value = getValue(context); - return (T)ExpressionUtils.convert(context, value, expectedResultType); + return ExpressionUtils.convert(context, value, expectedResultType); } public T getValue(Class expectedResultType) throws EvaluationException { Object value = getValue(); - return (T)ExpressionUtils.convert(null, value, expectedResultType); + return ExpressionUtils.convert(null, value, expectedResultType); } - public boolean isWritable(EvaluationContext context) throws EvaluationException { + public boolean isWritable(EvaluationContext context) { return false; } - public Class getValueType() throws EvaluationException { + public Class getValueType() { return String.class; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java index 5c1ed65f407..d0423925a41 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java @@ -1 +1 @@ -package org.springframework.expression.common; import java.util.LinkedList; import java.util.List; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; /** * An expression parser that understands templates. It can be subclassed by expression parsers that do not offer first * class support for templating. * * @author Keith Donald * @author Andy Clement */ public abstract class TemplateAwareExpressionParser implements ExpressionParser { public Expression parseExpression(String expressionString) throws ParseException { return parseExpression(expressionString, DefaultNonTemplateParserContext.INSTANCE); } public final Expression parseExpression(String expressionString, ParserContext context) throws ParseException { if (context == null) { context = DefaultNonTemplateParserContext.INSTANCE; } if (context.isTemplate()) { return parseTemplate(expressionString, context); } else { return doParseExpression(expressionString, context); } } private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException { if (expressionString.length() == 0) { // TODO throw exception if there are template prefix/suffixes and it is length 0? return new LiteralExpression(""); } Expression[] expressions = parseExpressions(expressionString, context); if (expressions.length == 1) { return expressions[0]; } else { return new CompositeStringExpression(expressionString, expressions); } } // helper methods /** * Helper that parses given expression string using the configured parser. The expression string can contain any * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static * pieces of text will also be returned as Expressions that just return that static piece of text. As a result, * evaluating all returned expressions and concatenating the results produces the complete evaluated string. * Unwrapping is only done of the outermost delimiters found, so the string 'hello ${foo${abc}}' would break into * the pieces 'hello ' and 'foo${abc}'. This means that expression languages that used ${..} as part of their * functionality are supported without any problem * * @param expressionString the expression string * @return the parsed expressions * @throws ParseException when the expressions cannot be parsed */ private final Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException { // TODO this needs to handle nested delimiters for cases where the expression uses the delim chars List expressions = new LinkedList(); int startIdx = 0; String prefix = context.getExpressionPrefix(); String suffix = context.getExpressionSuffix(); while (startIdx < expressionString.length()) { int prefixIndex = expressionString.indexOf(prefix, startIdx); if (prefixIndex >= startIdx) { // a inner expression was found - this is a composite if (prefixIndex > startIdx) { expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex))); startIdx = prefixIndex; } int nextPrefixIndex = expressionString.indexOf(prefix, prefixIndex + prefix.length()); int suffixIndex; if (nextPrefixIndex == -1) { // this is the last expression in the expression string suffixIndex = expressionString.lastIndexOf(suffix); } else { // another expression exists after this one in the expression string suffixIndex = expressionString.lastIndexOf(suffix, nextPrefixIndex); } if (suffixIndex < (prefixIndex + prefix.length())) { throw new ParseException(expressionString, "No ending suffix '" + suffix + "' for expression starting at character " + prefixIndex + ": " + expressionString.substring(prefixIndex), null); } else if (suffixIndex == prefixIndex + prefix.length()) { throw new ParseException(expressionString, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex, null); } else { String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex); expressions.add(doParseExpression(expr, context)); startIdx = suffixIndex + suffix.length(); } } else { if (startIdx == 0) { expressions.add(doParseExpression(expressionString, context)); } else { // no more ${expressions} found in string, add rest as static text expressions.add(new LiteralExpression(expressionString.substring(startIdx))); } startIdx = expressionString.length(); } } return expressions.toArray(new Expression[expressions.size()]); } protected abstract Expression doParseExpression(String expressionString, ParserContext context) throws ParseException; } \ No newline at end of file +/* * 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 java.util.LinkedList; import java.util.List; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; /** * 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 * @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 { // TODO this needs to handle nested delimiters for cases where the expression uses the delim chars List expressions = new LinkedList(); int startIdx = 0; String prefix = context.getExpressionPrefix(); String suffix = context.getExpressionSuffix(); while (startIdx < expressionString.length()) { int prefixIndex = expressionString.indexOf(prefix, startIdx); if (prefixIndex >= startIdx) { // a inner expression was found - this is a composite if (prefixIndex > startIdx) { expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex))); } int afterPrefixIndex = prefixIndex + prefix.length(); int suffixIndex = expressionString.indexOf(suffix, afterPrefixIndex); int nextPrefixIndex = expressionString.indexOf(prefix, afterPrefixIndex); if (nextPrefixIndex != -1 && suffixIndex > nextPrefixIndex) { throw new ParseException(expressionString, "No ending suffix '" + suffix + "' for expression starting at character " + prefixIndex + ": " + expressionString.substring(prefixIndex)); } else if (suffixIndex == afterPrefixIndex) { throw new ParseException(expressionString, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex); } else { String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex); expressions.add(doParseExpression(expr, context)); startIdx = suffixIndex + suffix.length(); } } else { // no more ${expressions} found in string, add rest as static text expressions.add(new LiteralExpression(expressionString.substring(startIdx))); startIdx = expressionString.length(); } } return expressions.toArray(new Expression[expressions.size()]); } /** * 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; } \ No newline at end of file diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateParserContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateParserContext.java new file mode 100644 index 00000000000..235b6f6a6d8 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateParserContext.java @@ -0,0 +1,58 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.common; + +import org.springframework.expression.ParserContext; + +/** + * Configurable {@link ParserContext} implementation for template parsing. + * Expects the expression prefix and suffix as constructor arguments. + * + * @author Juergen Hoeller + * @since 3.0 + */ +public class TemplateParserContext implements ParserContext { + + private final String expressionPrefix; + + private final String expressionSuffix; + + + /** + * Create a new TemplateParserContext for the given prefix and suffix. + * @param expressionPrefix the expression prefix to use + * @param expressionSuffix the expression suffix to use + */ + public TemplateParserContext(String expressionPrefix, String expressionSuffix) { + this.expressionPrefix = expressionPrefix; + this.expressionSuffix = expressionSuffix; + } + + + public final boolean isTemplate() { + return true; + } + + public final String getExpressionPrefix() { + return this.expressionPrefix; + } + + public final String getExpressionSuffix() { + return this.expressionSuffix; + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java index e897745206f..b0bace6209b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,6 @@ import org.springframework.expression.Operation; import org.springframework.expression.OperatorOverloader; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypeComparator; -import org.springframework.expression.TypeConverter; -import org.springframework.expression.TypeUtils; /** * An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other @@ -38,203 +36,158 @@ import org.springframework.expression.TypeUtils; * It also acts as a place for to define common utility routines that the various Ast nodes might need. * * @author Andy Clement + * @since 3.0 */ public class ExpressionState { - private EvaluationContext relatedContext; + private final EvaluationContext relatedContext; private final Stack variableScopes = new Stack(); private final Stack contextObjects = new Stack(); - public ExpressionState(EvaluationContext context) { - relatedContext = context; - createVariableScope(); - } public ExpressionState() { + this(null); + } + + public ExpressionState(EvaluationContext context) { + this.relatedContext = context; createVariableScope(); } + private void createVariableScope() { - variableScopes.add(new VariableScope()); // create an empty top level VariableScope + this.variableScopes.add(new VariableScope()); // create an empty top level VariableScope } /** * The active context object is what unqualified references to properties/etc are resolved against. */ public Object getActiveContextObject() { - if (contextObjects.isEmpty()) { - return relatedContext.getRootContextObject(); + if (this.contextObjects.isEmpty()) { + return this.relatedContext.getRootObject(); } - return contextObjects.peek(); + return this.contextObjects.peek(); } public void pushActiveContextObject(Object obj) { - contextObjects.push(obj); + this.contextObjects.push(obj); } public void popActiveContextObject() { - contextObjects.pop(); + this.contextObjects.pop(); } public Object getRootContextObject() { - return relatedContext.getRootContextObject(); - } - - public Object lookupReference(Object contextName, Object objectName) throws EvaluationException { - return relatedContext.lookupReference(contextName, objectName); - } - - public TypeUtils getTypeUtilities() { - return relatedContext.getTypeUtils(); - } - - public TypeComparator getTypeComparator() { - return relatedContext.getTypeUtils().getTypeComparator(); - } - - public Class findType(String type) throws EvaluationException { - return getTypeUtilities().getTypeLocator().findType(type); - } - - // TODO all these methods that grab the type converter will fail badly if there isn't one... - public boolean toBoolean(Object value) throws EvaluationException { - return ((Boolean) getTypeConverter().convertValue(value, Boolean.TYPE)).booleanValue(); - } - - public char toCharacter(Object value) throws EvaluationException { - return ((Character) getTypeConverter().convertValue(value, Character.TYPE)).charValue(); - } - - public short toShort(Object value) throws EvaluationException { - return ((Short) getTypeConverter().convertValue(value, Short.TYPE)).shortValue(); - } - - public int toInteger(Object value) throws EvaluationException { - return ((Integer) getTypeConverter().convertValue(value, Integer.TYPE)).intValue(); - } - - public double toDouble(Object value) throws EvaluationException { - return ((Double) getTypeConverter().convertValue(value, Double.TYPE)).doubleValue(); - } - - public float toFloat(Object value) throws EvaluationException { - return ((Float) getTypeConverter().convertValue(value, Float.TYPE)).floatValue(); - } - - public long toLong(Object value) throws EvaluationException { - return ((Long) getTypeConverter().convertValue(value, Long.TYPE)).longValue(); - } - - public byte toByte(Object value) throws EvaluationException { - return ((Byte) getTypeConverter().convertValue(value, Byte.TYPE)).byteValue(); - } - - public TypeConverter getTypeConverter() { - // TODO cache TypeConverter when it is set/changed? - return getTypeUtilities().getTypeConverter(); + return this.relatedContext.getRootObject(); } public void setVariable(String name, Object value) { - relatedContext.setVariable(name, value); + this.relatedContext.setVariable(name, value); } public Object lookupVariable(String name) { - return relatedContext.lookupVariable(name); + return this.relatedContext.lookupVariable(name); + } + + public Object lookupReference(Object contextName, String objectName) throws EvaluationException { + return this.relatedContext.lookupReference(contextName, objectName); + } + + public TypeComparator getTypeComparator() { + return this.relatedContext.getTypeComparator(); + } + + public Class findType(String type) throws EvaluationException { + return this.relatedContext.getTypeLocator().findType(type); + } + + public T convertValue(Object value, Class targetType) throws EvaluationException { + return this.relatedContext.getTypeConverter().convertValue(value, targetType); } /** * A new scope is entered when a function is invoked */ public void enterScope(Map argMap) { - variableScopes.push(new VariableScope(argMap)); + this.variableScopes.push(new VariableScope(argMap)); } public void enterScope(String name, Object value) { - variableScopes.push(new VariableScope(name, value)); + this.variableScopes.push(new VariableScope(name, value)); } public void exitScope() { - variableScopes.pop(); + this.variableScopes.pop(); } public void setLocalVariable(String name, Object value) { - variableScopes.peek().setVariable(name, value); + this.variableScopes.peek().setVariable(name, value); } public Object lookupLocalVariable(String name) { - int scopeNumber = variableScopes.size() - 1; + int scopeNumber = this.variableScopes.size() - 1; for (int i = scopeNumber; i >= 0; i--) { - if (variableScopes.get(i).definesVariable(name)) { - return variableScopes.get(i).lookupVariable(name); + if (this.variableScopes.get(i).definesVariable(name)) { + return this.variableScopes.get(i).lookupVariable(name); } } return null; } - public Object operate(Operation op, Object left, Object right) throws SpelException { - OperatorOverloader overloader = relatedContext.getTypeUtils().getOperatorOverloader(); - try { - if (overloader != null && overloader.overridesOperation(op, left, right)) { - return overloader.operate(op, left, right); - } else { - throw new SpelException(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, left, right); - } - } catch (EvaluationException e) { - if (e instanceof SpelException) { - throw (SpelException) e; - } else { - throw new SpelException(e, SpelMessages.UNEXPECTED_PROBLEM_INVOKING_OPERATOR, op, left, right, e - .getMessage()); - } + public Object operate(Operation op, Object left, Object right) throws EvaluationException { + OperatorOverloader overloader = this.relatedContext.getOperatorOverloader(); + if (overloader.overridesOperation(op, left, right)) { + return overloader.operate(op, left, right); + } + else { + throw new SpelException(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, left, right); } } public List getPropertyAccessors() { - return relatedContext.getPropertyAccessors(); + return this.relatedContext.getPropertyAccessors(); } public EvaluationContext getEvaluationContext() { - return relatedContext; + return this.relatedContext; } + /** * A new scope is entered when a function is called and it is used to hold the parameters to the function call. If the names * of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst * the function is executing. When the function returns the scope is exited. - * - * @author Andy Clement * */ - static class VariableScope { + private static class VariableScope { private final Map vars = new HashMap(); public VariableScope() { } public VariableScope(Map arguments) { - if (arguments!=null) { - vars.putAll(arguments); + if (arguments != null) { + this.vars.putAll(arguments); } } - public VariableScope(String name,Object value) { - vars.put(name,value); + public VariableScope(String name, Object value) { + this.vars.put(name,value); } public Object lookupVariable(String name) { - return vars.get(name); + return this.vars.get(name); } public void setVariable(String name, Object value) { - vars.put(name,value); + this.vars.put(name,value); } public boolean definesVariable(String name) { - return vars.containsKey(name); + return this.vars.containsKey(name); } - } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelException.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelException.java index 8ae54f36457..e2c702e9d8c 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelException.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelException.java @@ -23,7 +23,7 @@ import org.springframework.expression.EvaluationException; * that can occur. * * @author Andy Clement - * + * @since 3.0 */ public class SpelException extends EvaluationException { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java index 0c3a01a430d..f908edc7ebe 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpression.java @@ -26,18 +26,17 @@ import org.springframework.expression.common.ExpressionUtils; * asked to resolve references to types, beans, properties, methods. * * @author Andy Clement - * + * @since 3.0 */ public class SpelExpression implements Expression { private final String expression; + public final SpelNode ast; + /** * Construct an expression, only used by the parser. - * - * @param expression - * @param ast */ public SpelExpression(String expression, SpelNode ast) { this.expression = expression; @@ -48,26 +47,27 @@ public class SpelExpression implements Expression { * @return the expression string that was parsed to create this expression instance */ public String getExpressionString() { - return expression; + return this.expression; } /** * {@inheritDoc} */ public Object getValue() throws EvaluationException { - return ast.getValue(null); + return this.ast.getValue(null); } /** * {@inheritDoc} */ public Object getValue(EvaluationContext context) throws EvaluationException { - return ast.getValue(new ExpressionState(context)); + return this.ast.getValue(new ExpressionState(context)); } /** * {@inheritDoc} */ + @SuppressWarnings("unchecked") public T getValue(EvaluationContext context, Class expectedResultType) throws EvaluationException { Object result = ast.getValue(new ExpressionState(context)); @@ -75,31 +75,31 @@ public class SpelExpression implements Expression { Class resultType = result.getClass(); if (!expectedResultType.isAssignableFrom(resultType)) { // Attempt conversion to the requested type, may throw an exception - result = context.getTypeUtils().getTypeConverter().convertValue(result, expectedResultType); + result = context.getTypeConverter().convertValue(result, expectedResultType); } } - return (T)result; + return (T) result; } /** * {@inheritDoc} */ public void setValue(EvaluationContext context, Object value) throws EvaluationException { - ast.setValue(new ExpressionState(context), value); + this.ast.setValue(new ExpressionState(context), value); } /** * {@inheritDoc} */ public boolean isWritable(EvaluationContext context) throws EvaluationException { - return ast.isWritable(new ExpressionState(context)); + return this.ast.isWritable(new ExpressionState(context)); } /** * @return return the Abstract Syntax Tree for the expression */ public SpelNode getAST() { - return ast; + return this.ast; } /** @@ -110,7 +110,7 @@ public class SpelExpression implements Expression { * @return the string representation of the AST */ public String toStringAST() { - return ast.toStringAST(); + return this.ast.toStringAST(); } /** @@ -120,11 +120,7 @@ public class SpelExpression implements Expression { // TODO is this a legal implementation? The null return value could be very unhelpful. See other getValueType() // also. Object value = getValue(context); - if (value == null) { - return null; - } else { - return value.getClass(); - } + return (value != null ? value.getClass() : null); } /** @@ -132,20 +128,17 @@ public class SpelExpression implements Expression { */ public Class getValueType() throws EvaluationException { Object value = getValue(); - if (value == null) { - return null; - } else { - return value.getClass(); - } + return (value != null ? value.getClass() : null); } /** * {@inheritDoc} */ + @SuppressWarnings("unchecked") public T getValue(Class expectedResultType) throws EvaluationException { Object result = getValue(); // TODO propagate generic-ness into convert - return (T)ExpressionUtils.convert(null, result, expectedResultType); + return (T) ExpressionUtils.convert(null, result, expectedResultType); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpressionParser.java deleted file mode 100644 index 27cfadc8a3b..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelExpressionParser.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel; - -import org.springframework.expression.Expression; -import org.springframework.expression.ParseException; -import org.springframework.expression.ParserContext; -import org.springframework.expression.common.DefaultNonTemplateParserContext; -import org.springframework.expression.common.TemplateAwareExpressionParser; -import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; - -/** - * Instances of this parser class can process Spring Expression Language format expressions. The result of parsing an - * expression is a SpelExpression instance that can be repeatedly evaluated (possibly against different evaluation - * contexts) or serialized for later evaluation. - * - * @author Andy Clement - */ -public class SpelExpressionParser extends TemplateAwareExpressionParser { - - private final SpelInternalParser expressionParser; - - public SpelExpressionParser() { - // Use an Antlr based expression parser - expressionParser = new SpelAntlrExpressionParser(); - } - - /** - * Parse an expression string. - * - * @param expressionString the expression to parse - * @param context the parser context in which to perform the parse - * @return a parsed expression object - * @throws ParseException if the expression is invalid - */ - @Override - protected Expression doParseExpression(String expressionString, ParserContext context) throws ParseException { - return expressionParser.doParseExpression(expressionString,context); - } - - /** - * Simple override with covariance to return a nicer type - */ - @Override - public SpelExpression parseExpression(String expressionString) throws ParseException { - return (SpelExpression) super.parseExpression(expressionString, DefaultNonTemplateParserContext.INSTANCE); - } - - public interface SpelInternalParser { - Expression doParseExpression(String expressionString, ParserContext context) throws ParseException; - } -} \ No newline at end of file diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java index 666a30f5c34..08c3c51a57a 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import java.text.MessageFormat; @@ -33,7 +34,7 @@ import java.text.MessageFormat; * message has had all relevant inserts applied to it. * * @author Andy Clement - * + * @since 3.0 */ public enum SpelMessages { // TODO put keys and messages into bundles for easy NLS @@ -107,7 +108,7 @@ public enum SpelMessages { Kind.ERROR, 1060, "Expected the type of the new array to be specified as a String but found ''{0}''"), PROBLEM_DURING_TYPE_CONVERSION( Kind.ERROR, 1061, "Problem occurred during type conversion: {0}"), MULTIPLE_POSSIBLE_METHODS(Kind.ERROR, 1062, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"), EXCEPTION_DURING_PROPERTY_WRITE( - Kind.ERROR, 1063, "A problem occurred whilst attempting to set the property ''{0}'': ''{1}''"), NOT_AN_INTEGER( + Kind.ERROR, 1063, "A problem occurred whilst attempting to set the property ''{0}'': {1}"), NOT_AN_INTEGER( Kind.ERROR, 1064, "The value ''{0}'' cannot be parsed as an int"), NOT_A_LONG(Kind.ERROR, 1065, "The value ''{0}'' cannot be parsed as a long"), PARSE_PROBLEM(Kind.ERROR, 1066, "Error occurred during expression parse: {0}"), INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, @@ -121,9 +122,6 @@ public enum SpelMessages { private int code; private String message; - public static enum Kind { - INFO, WARNING, ERROR - }; private SpelMessages(Kind kind, int code, String message) { this.kind = kind; @@ -131,6 +129,7 @@ public enum SpelMessages { this.message = message; } + /** * Produce a complete message including the prefix, the position (if known) and with the inserts applied to the * message. @@ -160,4 +159,10 @@ public enum SpelMessages { formattedMessage.append(MessageFormat.format(message, inserts)); return formattedMessage.toString(); } + + + public static enum Kind { + INFO, WARNING, ERROR + } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelNode.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelNode.java index dbfed4520fb..b5db72c38c3 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelNode.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,20 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import org.springframework.expression.EvaluationException; /** * Represents a node in the Ast for a parsed expression. - * + * * @author Andy Clement + * @since 3.0 */ public interface SpelNode { /** * Evaluate the expression node in the context of the supplied expression state and return the value. - * * @param expressionState the current expression state (includes the context) * @return the value of this node evaluated against the specified state */ @@ -44,7 +45,6 @@ public interface SpelNode { /** * Evaluate the expression to a node and then set the new value on that node. For example, if the expression * evaluates to a property reference then the property will be set to the new value. - * * @param expressionState the current expression state (includes the context) * @param newValue the new value * @throws EvaluationException if any problem occurs evaluating the expression or setting the new value @@ -60,26 +60,23 @@ public interface SpelNode { * @return the number of children under this node */ int getChildCount(); - + /** * Helper method that returns a SpelNode rather than an Antlr Tree node. - * * @return the child node cast to a SpelNode */ SpelNode getChild(int index); /** * Determine the class of the object passed in, unless it is already a class object. - * * @param o the object that the caller wants the class of * @return the class of the object if it is not already a class object, or null if the object is null */ - Class getObjectClass(Object o); - - + Class getObjectClass(Object obj); + /** * @return the start position of this Ast node in the expression string */ - public int getStartPosition(); + int getStartPosition(); -} \ No newline at end of file +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedELException.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedSpelException.java similarity index 87% rename from org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedELException.java rename to org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedSpelException.java index 522b6cf4b1b..ea7bad24460 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedELException.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/WrappedSpelException.java @@ -22,9 +22,9 @@ package org.springframework.expression.spel; * * @author Andy Clement */ -public class WrappedELException extends RuntimeException { +public class WrappedSpelException extends RuntimeException { - public WrappedELException(SpelException e) { + public WrappedSpelException(SpelException e) { super(e); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpelAntlrExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpelAntlrExpressionParser.java index a1399e75fef..05c7adede23 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpelAntlrExpressionParser.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpelAntlrExpressionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,63 +13,70 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.antlr; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.RecognitionException; + import org.springframework.expression.Expression; import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; +import org.springframework.expression.common.TemplateAwareExpressionParser; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelExpression; import org.springframework.expression.spel.SpelNode; -import org.springframework.expression.spel.WrappedELException; -import org.springframework.expression.spel.SpelExpressionParser.SpelInternalParser; +import org.springframework.expression.spel.WrappedSpelException; import org.springframework.expression.spel.generated.SpringExpressionsLexer; import org.springframework.expression.spel.generated.SpringExpressionsParser.expr_return; /** - * Wrap an Antlr lexer and parser. - * + * Default {@link org.springframework.expression.ExpressionParser} implementation, + * wrapping an Antlr lexer and parser that implements standard Spring EL syntax. + * * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 */ -public class SpelAntlrExpressionParser implements SpelInternalParser { +public class SpelAntlrExpressionParser extends TemplateAwareExpressionParser { private final SpringExpressionsLexer lexer; + private final SpringExpressionsParserExtender parser; + public SpelAntlrExpressionParser() { - lexer = new SpringExpressionsLexerExtender(); - CommonTokenStream tokens = new CommonTokenStream(lexer); - parser = new SpringExpressionsParserExtender(tokens); + this.lexer = new SpringExpressionsLexerExtender(); + CommonTokenStream tokens = new CommonTokenStream(this.lexer); + this.parser = new SpringExpressionsParserExtender(tokens); } + /** * Parse an expression string. - * * @param expressionString the expression to parse * @param context the parser context in which to perform the parse * @return a parsed expression object * @throws ParseException if the expression is invalid */ - public Expression doParseExpression(String expressionString, ParserContext context) throws ParseException { + protected Expression doParseExpression(String expressionString, ParserContext context) throws ParseException { try { - lexer.setCharStream(new ANTLRStringStream(expressionString)); - CommonTokenStream tokens = new CommonTokenStream(lexer); - parser.setTokenStream(tokens); - expr_return exprReturn = parser.expr(); - SpelExpression newExpression = new SpelExpression(expressionString, (SpelNode) exprReturn.getTree()); - return newExpression; - } catch (RecognitionException re) { - ParseException exception = new ParseException(expressionString, "Recognition error at position: " - + re.charPositionInLine + ": " + re.getMessage(), re); - throw exception; - } catch (WrappedELException e) { - SpelException wrappedException = e.getCause(); - throw new ParseException(expressionString, "Parsing problem: " + wrappedException.getMessage(), - wrappedException); + this.lexer.setCharStream(new ANTLRStringStream(expressionString)); + CommonTokenStream tokens = new CommonTokenStream(this.lexer); + this.parser.setTokenStream(tokens); + expr_return exprReturn = this.parser.expr(); + return new SpelExpression(expressionString, (SpelNode) exprReturn.getTree()); + } + catch (RecognitionException re) { + throw new ParseException(expressionString, + "Recognition error at position: " + re.charPositionInLine + ": " + re.getMessage(), re); + } + catch (WrappedSpelException ex) { + SpelException wrappedException = ex.getCause(); + throw new ParseException(expressionString, + "Parsing problem: " + wrappedException.getMessage(), wrappedException); } } -} \ No newline at end of file +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsLexerExtender.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsLexerExtender.java index 67d260a44dd..127ae581cbb 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsLexerExtender.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsLexerExtender.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,19 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.antlr; import org.antlr.runtime.RecognitionException; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.WrappedELException; +import org.springframework.expression.spel.WrappedSpelException; import org.springframework.expression.spel.generated.SpringExpressionsLexer; -public class SpringExpressionsLexerExtender extends SpringExpressionsLexer { - - public SpringExpressionsLexerExtender() { - super(); - } +/** + * @author Andy Clement + * @since 3.0 + */ +class SpringExpressionsLexerExtender extends SpringExpressionsLexer { /** * recover() attempts to provide better error messages once something has gone wrong. It then throws a @@ -64,7 +65,7 @@ public class SpringExpressionsLexerExtender extends SpringExpressionsLexer { // getCharErrorDisplay(mte.expecting), getCharErrorDisplay(mte.c)); // } SpelException realException = new SpelException(re, SpelMessages.RECOGNITION_ERROR, re.toString()); - throw new WrappedELException(realException); + throw new WrappedSpelException(realException); } @Override diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsParserExtender.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsParserExtender.java index a7a7ce10ca9..0e4f390aee7 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsParserExtender.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/antlr/SpringExpressionsParserExtender.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.antlr; import org.antlr.runtime.BitSet; @@ -22,11 +23,15 @@ import org.antlr.runtime.Token; import org.antlr.runtime.TokenStream; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.WrappedELException; +import org.springframework.expression.spel.WrappedSpelException; import org.springframework.expression.spel.ast.SpelTreeAdaptor; import org.springframework.expression.spel.generated.SpringExpressionsParser; -public class SpringExpressionsParserExtender extends SpringExpressionsParser { +/** + * @author Andy Clement + * @since 3.0 + */ +class SpringExpressionsParserExtender extends SpringExpressionsParser { public SpringExpressionsParserExtender(TokenStream input) { super(input); @@ -53,7 +58,7 @@ public class SpringExpressionsParserExtender extends SpringExpressionsParser { // message = "no more input data to process whilst constructing " + paraphrase.peek(); // } SpelException parsingProblem = new SpelException(e.charPositionInLine, e, SpelMessages.PARSE_PROBLEM, message); - throw new WrappedELException(parsingProblem); + throw new WrappedSpelException(parsingProblem); } /** diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java index 719516c1c1e..95108dba366 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Assign.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -22,11 +23,11 @@ import org.springframework.expression.spel.ExpressionState; /** * Represents assignment. An alternative to calling setValue() for an expression is to use an assign. - *

- * Example: 'someNumberProperty=42' - * + * + *

Example: 'someNumberProperty=42' + * * @author Andy Clement - * + * @since 3.0 */ public class Assign extends SpelNodeImpl { @@ -51,4 +52,5 @@ public class Assign extends SpelNodeImpl { public boolean isWritable(ExpressionState expressionState) throws SpelException { return false; } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java index 791663e5408..8fc93bdeb6c 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,15 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; /** * Represents the literal values TRUE and FALSE. - * + * * @author Andy Clement - * + * @since 3.0 */ public class BooleanLiteral extends Literal { @@ -34,7 +35,7 @@ public class BooleanLiteral extends Literal { @Override public Boolean getLiteralValue() { - return value; + return this.value; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java index cad04cf8fe5..310061cd486 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -24,7 +25,7 @@ import org.springframework.expression.spel.ExpressionState; * Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()' * * @author Andy Clement - * + * @since 3.0 */ public class CompoundExpression extends SpelNodeImpl { @@ -35,7 +36,6 @@ public class CompoundExpression extends SpelNodeImpl { /** * Evalutes a compound expression. This involves evaluating each piece in turn and the return value from each piece * is the active context object for the subsequent piece. - * * @param state the state in which the expression is being evaluated * @return the final value from the last piece of the compound expression */ diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java index 89681b28f73..a62ec16aa75 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.lang.reflect.Array; import java.util.List; import org.antlr.runtime.Token; + import org.springframework.expression.AccessException; import org.springframework.expression.ConstructorExecutor; import org.springframework.expression.ConstructorResolver; @@ -27,8 +29,6 @@ import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.internal.TypeCode; -import org.springframework.expression.spel.internal.Utils; /** * Represents the invocation of a constructor. Either a constructor on a regular type or construction of an array. When @@ -40,41 +40,38 @@ import org.springframework.expression.spel.internal.Utils; * new int[3] new int[3]{1,2,3} * * @author Andy Clement - * + * @author Juergen Hoeller + * @since 3.0 */ public class ConstructorReference extends SpelNodeImpl { - /** - * The resolver/executor model {@link ConstructorResolver} supports the caching of executor objects that can run - * some discovered constructor repeatedly without searching for it each time. This flag controls whether caching - * occurs and is primarily exposed for testing. - */ - public static boolean useCaching = true; - - /** - * The cached executor that may be reused on subsequent evaluations. - */ - private ConstructorExecutor cachedExecutor; - /** * If true then this is an array constructor, for example, 'new String[]', rather than a simple constructor 'new * String()' */ private final boolean isArrayConstructor; + /** + * The cached executor that may be reused on subsequent evaluations. + */ + private ConstructorExecutor cachedExecutor; + + public ConstructorReference(Token payload, boolean isArrayConstructor) { super(payload); this.isArrayConstructor = isArrayConstructor; } + /** * Implements getValue() - delegating to the code for building an array or a simple type. */ @Override public Object getValueInternal(ExpressionState state) throws EvaluationException { - if (isArrayConstructor) { + if (this.isArrayConstructor) { return createArray(state); - } else { + } + else { return createNewInstance(state); } } @@ -87,7 +84,7 @@ public class ConstructorReference extends SpelNodeImpl { int index = 0; sb.append(getChild(index++).toStringAST()); - if (!isArrayConstructor) { + if (!this.isArrayConstructor) { sb.append("("); for (int i = index; i < getChildCount(); i++) { if (i > index) @@ -95,7 +92,8 @@ public class ConstructorReference extends SpelNodeImpl { sb.append(getChild(i).toStringAST()); } sb.append(")"); - } else { + } + else { // Next child is EXPRESSIONLIST token with children that are the // expressions giving array size sb.append("["); @@ -117,10 +115,10 @@ public class ConstructorReference extends SpelNodeImpl { return false; } + /** * Create an array and return it. The children of this node indicate the type of array, the array ranks and any * optional initializer that might have been supplied. - * * @param state the expression state within which this expression is being evaluated * @return the new array * @throws EvaluationException if there is a problem creating the array @@ -129,8 +127,8 @@ public class ConstructorReference extends SpelNodeImpl { Object intendedArrayType = getChild(0).getValueInternal(state); if (!(intendedArrayType instanceof String)) { throw new SpelException(getChild(0).getCharPositionInLine(), - SpelMessages.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION, Utils - .formatClassnameForMessage(intendedArrayType.getClass())); + SpelMessages.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION, + FormatHelper.formatClassNameForMessage(intendedArrayType.getClass())); } String type = (String) intendedArrayType; Class componentType = null; @@ -151,12 +149,13 @@ public class ConstructorReference extends SpelNodeImpl { // no array ranks so use the size of the initializer to determine array size int arraySize = getChild(2).getChildCount(); newArray = Array.newInstance(componentType, arraySize); - } else { + } + else { // Array ranks are specified but is it a single or multiple dimension array? int dimensions = getChild(1).getChildCount(); if (dimensions == 1) { Object o = getChild(1).getValueInternal(state); - int arraySize = state.toInteger(o); + int arraySize = state.convertValue(o, Integer.class); if (getChildCount() == 3) { // Check initializer length matches array size length int initializerLength = getChild(2).getChildCount(); @@ -166,11 +165,12 @@ public class ConstructorReference extends SpelNodeImpl { } } newArray = Array.newInstance(componentType, arraySize); - } else { + } + else { // Multi-dimensional - hold onto your hat ! int[] dims = new int[dimensions]; for (int d = 0; d < dimensions; d++) { - dims[d] = state.toInteger(getChild(1).getChild(d).getValueInternal(state)); + dims[d] = state.convertValue(getChild(1).getChild(d).getValueInternal(state), Integer.class); } newArray = Array.newInstance(componentType, dims); // TODO check any specified initializer for the multidim array matches @@ -195,42 +195,42 @@ public class ConstructorReference extends SpelNodeImpl { } else if (arrayTypeCode == TypeCode.INT) { int[] newIntArray = (int[]) newArray; for (int i = 0; i < newIntArray.length; i++) { - newIntArray[i] = state.toInteger(initializer.getChild(i).getValueInternal(state)); + newIntArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Integer.class); } } else if (arrayTypeCode == TypeCode.BOOLEAN) { boolean[] newBooleanArray = (boolean[]) newArray; for (int i = 0; i < newBooleanArray.length; i++) { - newBooleanArray[i] = state.toBoolean(initializer.getChild(i).getValueInternal(state)); + newBooleanArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Boolean.class); } } else if (arrayTypeCode == TypeCode.CHAR) { char[] newCharArray = (char[]) newArray; for (int i = 0; i < newCharArray.length; i++) { - newCharArray[i] = state.toCharacter(initializer.getChild(i).getValueInternal(state)); + newCharArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Character.class); } } else if (arrayTypeCode == TypeCode.SHORT) { short[] newShortArray = (short[]) newArray; for (int i = 0; i < newShortArray.length; i++) { - newShortArray[i] = state.toShort(initializer.getChild(i).getValueInternal(state)); + newShortArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Short.class); } } else if (arrayTypeCode == TypeCode.LONG) { long[] newLongArray = (long[]) newArray; for (int i = 0; i < newLongArray.length; i++) { - newLongArray[i] = state.toLong(initializer.getChild(i).getValueInternal(state)); + newLongArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Long.class); } } else if (arrayTypeCode == TypeCode.FLOAT) { float[] newFloatArray = (float[]) newArray; for (int i = 0; i < newFloatArray.length; i++) { - newFloatArray[i] = state.toFloat(initializer.getChild(i).getValueInternal(state)); + newFloatArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Float.class); } } else if (arrayTypeCode == TypeCode.DOUBLE) { double[] newDoubleArray = (double[]) newArray; for (int i = 0; i < newDoubleArray.length; i++) { - newDoubleArray[i] = state.toDouble(initializer.getChild(i).getValueInternal(state)); + newDoubleArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Double.class); } } else if (arrayTypeCode == TypeCode.BYTE) { byte[] newByteArray = (byte[]) newArray; for (int i = 0; i < newByteArray.length; i++) { - newByteArray[i] = state.toByte(initializer.getChild(i).getValueInternal(state)); + newByteArray[i] = state.convertValue(initializer.getChild(i).getValueInternal(state), Byte.class); } } } @@ -240,7 +240,6 @@ public class ConstructorReference extends SpelNodeImpl { /** * Create a new ordinary object and return it. - * * @param state the expression state within which this expression is being evaluated * @return the new object * @throws EvaluationException if there is a problem creating the object @@ -254,41 +253,41 @@ public class ConstructorReference extends SpelNodeImpl { argumentTypes[i] = childValue.getClass(); } - if (cachedExecutor != null) { + ConstructorExecutor executorToUse = this.cachedExecutor; + if (executorToUse != null) { try { - return cachedExecutor.execute(state.getEvaluationContext(), arguments); - } catch (AccessException ae) { + return executorToUse.execute(state.getEvaluationContext(), arguments); + } + catch (AccessException ae) { // this is OK - it may have gone stale due to a class change, // let's try to get a new one and call it before giving up + this.cachedExecutor = null; } } // either there was no accessor or it no longer exists String typename = (String) getChild(0).getValueInternal(state); - cachedExecutor = findExecutorForConstructor(typename, argumentTypes, state); + executorToUse = findExecutorForConstructor(typename, argumentTypes, state); try { - return cachedExecutor.execute(state.getEvaluationContext(), arguments); - } catch (AccessException ae) { + return executorToUse.execute(state.getEvaluationContext(), arguments); + } + catch (AccessException ae) { throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_CONSTRUCTOR_INVOCATION, typename, ae.getMessage()); - } finally { - if (!useCaching) { - cachedExecutor = null; - } } } /** * Go through the list of registered constructor resolvers and see if any can find a constructor that takes the * specified set of arguments. - * * @param typename the type trying to be constructed * @param argumentTypes the types of the arguments supplied that the constructor must take * @param state the current state of the expression * @return a reusable ConstructorExecutor that can be invoked to run the constructor or null * @throws SpelException if there is a problem locating the constructor */ - public ConstructorExecutor findExecutorForConstructor(String typename, Class[] argumentTypes, - ExpressionState state) throws SpelException { + protected ConstructorExecutor findExecutorForConstructor( + String typename, Class[] argumentTypes, ExpressionState state) throws SpelException { + EvaluationContext eContext = state.getEvaluationContext(); List cResolvers = eContext.getConstructorResolvers(); if (cResolvers != null) { @@ -299,18 +298,14 @@ public class ConstructorReference extends SpelNodeImpl { if (cEx != null) { return cEx; } - } catch (AccessException e) { - Throwable cause = e.getCause(); - if (cause instanceof SpelException) { - throw (SpelException) cause; - } else { - throw new SpelException(cause, SpelMessages.PROBLEM_LOCATING_CONSTRUCTOR, typename, Utils - .formatMethodForMessage("", argumentTypes)); - } + } + catch (AccessException ex) { + throw new SpelException(ex, SpelMessages.PROBLEM_LOCATING_CONSTRUCTOR, typename, + FormatHelper.formatMethodForMessage("", argumentTypes)); } } } - throw new SpelException(SpelMessages.CONSTRUCTOR_NOT_FOUND, typename, Utils.formatMethodForMessage("", + throw new SpelException(SpelMessages.CONSTRUCTOR_NOT_FOUND, typename, FormatHelper.formatMethodForMessage("", argumentTypes)); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Dot.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Dot.java index e620dfc9797..4fc79e58aea 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Dot.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Dot.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -21,9 +22,9 @@ import org.springframework.expression.spel.ExpressionState; /** * This is used for preserving positional information from the input expression. - * + * * @author Andy Clement - * + * @since 3.0 */ public class Dot extends SpelNodeImpl { // TODO Keep Dot for the positional information or remove it? diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/EmptySpelNode.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/EmptySpelNode.java index 13ffe7bba2e..1ded0a404e9 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/EmptySpelNode.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/EmptySpelNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,12 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.ExpressionState; +/** + * @author Andy Clement + * @since 3.0 + */ public class EmptySpelNode extends SpelNodeImpl { public EmptySpelNode(Token payload) { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/Utils.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java similarity index 86% rename from org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/Utils.java rename to org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java index 3df8695398c..a0b0eea3c3b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/Utils.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.spel.internal; + +package org.springframework.expression.spel.ast; /** * Utility methods (formatters, etc) used during parsing and evaluation. * * @author Andy Clement */ -public class Utils { +class FormatHelper { /** * Produce a nice string for a given method name with specified arguments. - * * @param name the name of the method * @param argumentTypes the types of the arguments to the method * @return nicely formatted string, eg. foo(String,int) @@ -35,8 +35,9 @@ public class Utils { sb.append("("); if (argumentTypes != null) { for (int i = 0; i < argumentTypes.length; i++) { - if (i > 0) + if (i > 0) { sb.append(","); + } sb.append(argumentTypes[i].getName()); } } @@ -47,12 +48,11 @@ public class Utils { /** * Produce a nice string for a given class object. For example a string array will have the formatted name * "java.lang.String[]". - * * @param clazz The class whose name is to be formatted * @return a formatted string suitable for message inclusion */ - public static String formatClassnameForMessage(Class clazz) { - if (clazz==null) { + public static String formatClassNameForMessage(Class clazz) { + if (clazz == null) { return "null"; } StringBuilder fmtd = new StringBuilder(); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java index e34ed4064e2..d09bb94f4e7 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.lang.reflect.InvocationTargetException; @@ -20,13 +21,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import org.antlr.runtime.Token; -import org.springframework.expression.EvaluationContext; + import org.springframework.expression.EvaluationException; import org.springframework.expression.TypeConverter; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.reflection.ReflectionUtils; +import org.springframework.expression.spel.support.ReflectionHelper; /** * A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in the context prior to the @@ -38,16 +39,19 @@ import org.springframework.expression.spel.reflection.ReflectionUtils; * Functions are very simplistic, the arguments are not part of the definition (right now), so the names must be unique. * * @author Andy Clement + * @since 3.0 */ public class FunctionReference extends SpelNodeImpl { private final String name; + public FunctionReference(Token payload) { super(payload); - name = payload.getText(); + this.name = payload.getText(); } + @Override public Object getValueInternal(ExpressionState state) throws EvaluationException { Object o = state.lookupVariable(name); @@ -87,15 +91,11 @@ public class FunctionReference extends SpelNodeImpl { // Convert arguments if necessary and remap them for varargs if required if (functionArgs != null) { - EvaluationContext ctx = state.getEvaluationContext(); - TypeConverter converter = null; - if (ctx.getTypeUtils() != null) { - converter = ctx.getTypeUtils().getTypeConverter(); - } - ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, functionArgs); + TypeConverter converter = state.getEvaluationContext().getTypeConverter(); + ReflectionHelper.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, functionArgs); } if (m.isVarArgs()) { - functionArgs = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), functionArgs); + functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(m.getParameterTypes(), functionArgs); } try { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java index 54cb182586f..595443afea6 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,29 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; -import org.springframework.expression.spel.SpelException; -import org.springframework.expression.spel.ExpressionState; +import org.springframework.expression.spel.ExpressionState; +import org.springframework.expression.spel.SpelException; + +/** + * @author Andy Clement + * @since 3.0 + */ public class Identifier extends SpelNodeImpl { private final String id; public Identifier(Token payload) { super(payload); - id = payload.getText(); + this.id = payload.getText(); } @Override public String toStringAST() { - return id; + return this.id; } @Override - public String getValueInternal(ExpressionState state) throws SpelException { - return id; + public String getValueInternal(ExpressionState state) { + return this.id; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index e784e0c367c..fe605e88d25 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.util.Collection; @@ -31,6 +32,7 @@ import org.springframework.expression.spel.SpelMessages; * strings/collections (lists/sets)/arrays * * @author Andy Clement + * @since 3.0 */ public class Indexer extends SpelNodeImpl { @@ -48,7 +50,7 @@ public class Indexer extends SpelNodeImpl { return ((Map) ctx).get(index); } - int idx = state.toInteger(index); + int idx = state.convertValue(index, Integer.class); if (ctx.getClass().isArray()) { return accessArrayElement(ctx, idx); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java index dfc0be7ad13..8ee2f84cf33 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; /** * Expression language AST node that represents an integer literal. - * + * * @author Andy Clement + * @since 3.0 */ public class IntLiteral extends Literal { @@ -33,7 +35,7 @@ public class IntLiteral extends Literal { @Override public Integer getLiteralValue() { - return value; + return this.value; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/KeyValuePair.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/KeyValuePair.java similarity index 86% rename from org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/KeyValuePair.java rename to org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/KeyValuePair.java index ee15d037c83..3fdc10bb727 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/KeyValuePair.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/KeyValuePair.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.spel.internal; + +package org.springframework.expression.spel.ast; /** * Special object that is used to wrap a map entry/value when iterating over a map. Providing a direct way for the @@ -21,7 +22,8 @@ package org.springframework.expression.spel.internal; * * @author Andy Clement */ -public class KeyValuePair { +class KeyValuePair { + public Object key; public Object value; diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java index 01ba7f1ec30..9efe1064a7b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Literal.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.WrappedELException; +import org.springframework.expression.spel.WrappedSpelException; /** * Common superclass for nodes representing literals (boolean, string, number, etc). @@ -84,7 +85,7 @@ public abstract class Literal extends SpelNodeImpl { long value = Long.parseLong(numberString, radix); return new LongLiteral(numberToken, value); } catch (NumberFormatException nfe) { - throw new WrappedELException(new SpelException(numberToken.getCharPositionInLine(), nfe, + throw new WrappedSpelException(new SpelException(numberToken.getCharPositionInLine(), nfe, SpelMessages.NOT_A_LONG, numberToken.getText())); } } else { @@ -92,7 +93,7 @@ public abstract class Literal extends SpelNodeImpl { int value = Integer.parseInt(numberString, radix); return new IntLiteral(numberToken, value); } catch (NumberFormatException nfe) { - throw new WrappedELException(new SpelException(numberToken.getCharPositionInLine(), nfe, + throw new WrappedSpelException(new SpelException(numberToken.getCharPositionInLine(), nfe, SpelMessages.NOT_AN_INTEGER, numberToken.getText())); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java index 01654815b77..7d4c597fcd7 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; /** * Expression language AST node that represents a long integer literal. - * + * * @author Andy Clement + * @since 3.0 */ public class LongLiteral extends Literal { @@ -33,7 +35,7 @@ public class LongLiteral extends Literal { @Override public Long getLiteralValue() { - return value; + return this.value; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 22f3af34eaa..d82348a2a67 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.util.List; import org.antlr.runtime.Token; + import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; @@ -26,18 +28,25 @@ import org.springframework.expression.MethodResolver; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.internal.Utils; +/** + * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 + */ public class MethodReference extends SpelNodeImpl { private final String name; - private MethodExecutor fastInvocationAccessor; + + private volatile MethodExecutor cachedExecutor; + public MethodReference(Token payload) { super(payload); name = payload.getText(); } + @Override public Object getValueInternal(ExpressionState state) throws EvaluationException { Object currentContext = state.getActiveContextObject(); @@ -50,30 +59,36 @@ public class MethodReference extends SpelNodeImpl { formatMethodForMessage(name, getTypes(arguments))); } - if (fastInvocationAccessor != null) { + MethodExecutor executorToUse = this.cachedExecutor; + if (executorToUse != null) { try { - return fastInvocationAccessor.execute(state.getEvaluationContext(), state.getActiveContextObject(), - arguments); - } catch (AccessException ae) { - // this is OK - it may have gone stale due to a class change, let's get a new one and retry before - // giving up + return executorToUse.execute( + state.getEvaluationContext(), state.getActiveContextObject(), arguments); + } + catch (AccessException ae) { + // this is OK - it may have gone stale due to a class change, + // let's try to get a new one and call it before giving up + this.cachedExecutor = null; } } + // either there was no accessor or it no longer existed - fastInvocationAccessor = findAccessorForMethod(name, getTypes(arguments), state); + executorToUse = findAccessorForMethod(this.name, getTypes(arguments), state); + this.cachedExecutor = executorToUse; try { - return fastInvocationAccessor.execute(state.getEvaluationContext(), state.getActiveContextObject(), - arguments); - } catch (AccessException ae) { - ae.printStackTrace(); - throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_METHOD_INVOCATION, name, - state.getActiveContextObject().getClass().getName(), ae.getMessage()); + return executorToUse.execute( + state.getEvaluationContext(), state.getActiveContextObject(), arguments); + } + catch (AccessException ae) { + throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_METHOD_INVOCATION, + this.name, state.getActiveContextObject().getClass().getName(), ae.getMessage()); } } private Class[] getTypes(Object... arguments) { - if (arguments == null) + if (arguments == null) { return null; + } Class[] argumentTypes = new Class[arguments.length]; for (int i = 0; i < arguments.length; i++) { argumentTypes[i] = arguments[i].getClass(); @@ -120,37 +135,33 @@ public class MethodReference extends SpelNodeImpl { return false; } - public final MethodExecutor findAccessorForMethod(String name, Class[] argumentTypes, ExpressionState state) + + protected MethodExecutor findAccessorForMethod(String name, Class[] argumentTypes, ExpressionState state) throws SpelException { + Object contextObject = state.getActiveContextObject(); EvaluationContext eContext = state.getEvaluationContext(); if (contextObject == null) { - throw new SpelException(SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT, Utils - .formatMethodForMessage(name, argumentTypes)); + throw new SpelException(SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT, + FormatHelper.formatMethodForMessage(name, argumentTypes)); } List mResolvers = eContext.getMethodResolvers(); if (mResolvers != null) { for (MethodResolver methodResolver : mResolvers) { try { - MethodExecutor cEx = methodResolver.resolve(state.getEvaluationContext(), contextObject, name, - argumentTypes); - if (cEx != null) + MethodExecutor cEx = methodResolver.resolve( + state.getEvaluationContext(), contextObject, name, argumentTypes); + if (cEx != null) { return cEx; - } catch (AccessException e) { - Throwable cause = e.getCause(); - if (cause instanceof SpelException) { - throw (SpelException) cause; - } else { - throw new SpelException(cause, SpelMessages.PROBLEM_LOCATING_METHOD, name, contextObject - .getClass()); } } + catch (AccessException ex) { + throw new SpelException(ex, SpelMessages.PROBLEM_LOCATING_METHOD, name, contextObject.getClass()); + } } } - throw new SpelException(SpelMessages.METHOD_NOT_FOUND, Utils.formatMethodForMessage(name, argumentTypes), Utils - .formatClassnameForMessage(contextObject instanceof Class ? ((Class) contextObject) : contextObject - .getClass())); - // (contextObject instanceof Class ? ((Class) contextObject).getName() : contextObject.getClass() - // .getName())); + throw new SpelException(SpelMessages.METHOD_NOT_FOUND, FormatHelper.formatMethodForMessage(name, argumentTypes), + FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class) contextObject) : contextObject.getClass())); } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java index 62e0a41749e..fea5a7d3a5b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; +/** + * @author Andy Clement + * @since 3.0 + */ public class NullLiteral extends Literal { public NullLiteral(Token payload) { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java index b77139f739b..efa06f7fe8f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Operator.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -22,8 +23,9 @@ import org.springframework.expression.spel.ExpressionState; /** * Common supertype for operators that operate on either one or two operands. In the case of multiply or divide there * would be two operands, but for unary plus or minus, there is only one. - * + * * @author Andy Clement + * @since 3.0 */ public abstract class Operator extends SpelNodeImpl { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorAnd.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorAnd.java index 55726e3135c..9217a44f235 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorAnd.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorAnd.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -22,8 +23,9 @@ import org.springframework.expression.spel.ExpressionState; /** * Represents the boolean AND operation. - * + * * @author Andy Clement + * @since 3.0 */ public class OperatorAnd extends Operator { @@ -42,8 +44,9 @@ public class OperatorAnd extends Operator { boolean rightValue; try { - leftValue = state.toBoolean(getLeftOperand().getValueInternal(state)); - } catch (SpelException ee) { + leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class); + } + catch (SpelException ee) { ee.setPosition(getLeftOperand().getCharPositionInLine()); throw ee; } @@ -53,8 +56,9 @@ public class OperatorAnd extends Operator { } try { - rightValue = state.toBoolean(getRightOperand().getValueInternal(state)); - } catch (SpelException ee) { + rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class); + } + catch (SpelException ee) { ee.setPosition(getRightOperand().getCharPositionInLine()); throw ee; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java index ff6793e5db5..cba9549edff 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.util.List; @@ -30,6 +31,7 @@ import org.springframework.expression.spel.SpelMessages; * in the list. The definition of between being inclusive follows the SQL BETWEEN definition. * * @author Andy Clement + * @since 3.0 */ public class OperatorBetween extends Operator { @@ -45,7 +47,6 @@ public class OperatorBetween extends Operator { /** * Returns a boolean based on whether a value is in the range expressed. The first operand is any value whilst the * second is a list of two values - those two values being the bounds allowed for the first operand (inclusive). - * * @param state the expression state * @return true if the left operand is in the range specified, false otherwise * @throws EvaluationException if there is a problem evaluating the expression diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorDivide.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorDivide.java index 6b9b913072a..40bea4f079e 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorDivide.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorDivide.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -21,9 +22,10 @@ import org.springframework.expression.Operation; import org.springframework.expression.spel.ExpressionState; /** - * Implements division operator - * + * Implements division operator. + * * @author Andy Clement + * @since 3.0 */ public class OperatorDivide extends Operator { @@ -44,17 +46,16 @@ public class OperatorDivide extends Operator { Number op1 = (Number) operandOne; Number op2 = (Number) operandTwo; if (op1 instanceof Double || op2 instanceof Double) { - Double result = op1.doubleValue() / op2.doubleValue(); - return result; - } else if (op1 instanceof Float || op2 instanceof Float) { - Float result = op1.floatValue() / op2.floatValue(); - return result; - } else if (op1 instanceof Long || op2 instanceof Long) { - Long result = op1.longValue() / op2.longValue(); - return result; - } else { // TODO what about non-int result of the division? - Integer result = op1.intValue() / op2.intValue(); - return result; + return op1.doubleValue() / op2.doubleValue(); + } + else if (op1 instanceof Float || op2 instanceof Float) { + return op1.floatValue() / op2.floatValue(); + } + else if (op1 instanceof Long || op2 instanceof Long) { + return op1.longValue() / op2.longValue(); + } + else { // TODO what about non-int result of the division? + return op1.intValue() / op2.intValue(); } } return state.operate(Operation.DIVIDE, operandOne, operandTwo); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorEquality.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorEquality.java index 8eb1ce3a64b..377c2a5c4a6 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorEquality.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorEquality.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -21,8 +22,9 @@ import org.springframework.expression.spel.ExpressionState; /** * Implements equality operator. - * + * * @author Andy Clement + * @since 3.0 */ public class OperatorEquality extends Operator { @@ -44,11 +46,14 @@ public class OperatorEquality extends Operator { Number op2 = (Number) right; if (op1 instanceof Double || op2 instanceof Double) { return op1.doubleValue() == op2.doubleValue(); - } else if (op1 instanceof Float || op2 instanceof Float) { + } + else if (op1 instanceof Float || op2 instanceof Float) { return op1.floatValue() == op2.floatValue(); - } else if (op1 instanceof Long || op2 instanceof Long) { + } + else if (op1 instanceof Long || op2 instanceof Long) { return op1.longValue() == op2.longValue(); - } else { + } + else { return op1.intValue() == op2.intValue(); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThan.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThan.java index 5a8168cddc5..ab4eb48ae53 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThan.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThan.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; /** - * Implements greater than operator. - * + * Implements greater-than operator. + * * @author Andy Clement + * @since 3.0 */ public class OperatorGreaterThan extends Operator { @@ -44,11 +46,14 @@ public class OperatorGreaterThan extends Operator { Number op2 = (Number) right; if (op1 instanceof Double || op2 instanceof Double) { return op1.doubleValue() > op2.doubleValue(); - } else if (op1 instanceof Float || op2 instanceof Float) { + } + else if (op1 instanceof Float || op2 instanceof Float) { return op1.floatValue() > op2.floatValue(); - } else if (op1 instanceof Long || op2 instanceof Long) { + } + else if (op1 instanceof Long || op2 instanceof Long) { return op1.longValue() > op2.longValue(); - } else { + } + else { return op1.intValue() > op2.intValue(); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThanOrEqual.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThanOrEqual.java index ae03223950a..44288ca0409 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThanOrEqual.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThanOrEqual.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; /** - * Implements greater than or equal operator. - * + * Implements greater-than-or-equal operator. + * * @author Andy Clement + * @since 3.0 */ public class OperatorGreaterThanOrEqual extends Operator { @@ -44,11 +46,14 @@ public class OperatorGreaterThanOrEqual extends Operator { Number op2 = (Number) right; if (op1 instanceof Double || op2 instanceof Double) { return op1.doubleValue() >= op2.doubleValue(); - } else if (op1 instanceof Float || op2 instanceof Float) { + } + else if (op1 instanceof Float || op2 instanceof Float) { return op1.floatValue() >= op2.floatValue(); - } else if (op1 instanceof Long || op2 instanceof Long) { + } + else if (op1 instanceof Long || op2 instanceof Long) { return op1.longValue() >= op2.longValue(); - } else { + } + else { return op1.intValue() >= op2.intValue(); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInequality.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInequality.java index ee02fe1eaf2..4f3fd165485 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInequality.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInequality.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; /** - * Implements the not-equal operator - * + * Implements the not-equal operator. + * * @author Andy Clement + * @since 3.0 */ public class OperatorInequality extends Operator { @@ -44,11 +46,14 @@ public class OperatorInequality extends Operator { Number op2 = (Number) right; if (op1 instanceof Double || op2 instanceof Double) { return op1.doubleValue() != op2.doubleValue(); - } else if (op1 instanceof Float || op2 instanceof Float) { + } + else if (op1 instanceof Float || op2 instanceof Float) { return op1.floatValue() != op2.floatValue(); - } else if (op1 instanceof Long || op2 instanceof Long) { + } + else if (op1 instanceof Long || op2 instanceof Long) { return op1.longValue() != op2.longValue(); - } else { + } + else { return op1.intValue() != op2.intValue(); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java index 56af41a78f3..684bb95459f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,19 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; + import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; /** - * The operator 'instanceof' checks if an object is of the class specified in the right hand operand, in the same way - * that instanceof does in Java. - * + * The operator 'instanceof' checks if an object is of the class specified in the right hand operand, + * in the same way that instanceof does in Java. + * * @author Andy Clement + * @since 3.0 */ public class OperatorInstanceof extends Operator { @@ -39,9 +42,8 @@ public class OperatorInstanceof extends Operator { } /** - * Compare the left operand to see it is an instance of the type specified as the right operand. The right operand - * must be a class. - * + * Compare the left operand to see it is an instance of the type specified as the right operand. + * The right operand must be a class. * @param state the expression state * @return true if the left operand is an instanceof of the right operand, otherwise false * @throws EvaluationException if there is a problem evaluating the expression @@ -51,11 +53,12 @@ public class OperatorInstanceof extends Operator { Object left = getLeftOperand().getValueInternal(state); Object right = getRightOperand().getValueInternal(state); if (left == null) { - return false; // null is not an instanceof anything + return false; // null is not an instanceof anything } if (right == null || !(right instanceof Class)) { throw new SpelException(getRightOperand().getCharPositionInLine(), - SpelMessages.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, (right == null ? "null" : right.getClass().getName())); + SpelMessages.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, + (right == null ? "null" : right.getClass().getName())); } Class rightClass = (Class) right; return rightClass.isAssignableFrom(left.getClass()); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThan.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThan.java index fd3ab2c9aee..cc8725adbc4 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThan.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThan.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; /** - * Implements the less than operator - * + * Implements the less-than operator. + * * @author Andy Clement + * @since 3.0 */ public class OperatorLessThan extends Operator { @@ -44,11 +46,14 @@ public class OperatorLessThan extends Operator { Number op2 = (Number) right; if (op1 instanceof Double || op2 instanceof Double) { return op1.doubleValue() < op2.doubleValue(); - } else if (op1 instanceof Float || op2 instanceof Float) { + } + else if (op1 instanceof Float || op2 instanceof Float) { return op1.floatValue() < op2.floatValue(); - } else if (op1 instanceof Long || op2 instanceof Long) { + } + else if (op1 instanceof Long || op2 instanceof Long) { return op1.longValue() < op2.longValue(); - } else { + } + else { return op1.intValue() < op2.intValue(); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThanOrEqual.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThanOrEqual.java index a984b87680f..f9afba8ffc4 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThanOrEqual.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLessThanOrEqual.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -20,9 +21,10 @@ import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; /** - * Implements the less than or equal operator - * + * Implements the less-than-or-equal operator. + * * @author Andy Clement + * @since 3.0 */ public class OperatorLessThanOrEqual extends Operator { @@ -39,11 +41,14 @@ public class OperatorLessThanOrEqual extends Operator { Number op2 = (Number) right; if (op1 instanceof Double || op2 instanceof Double) { return op1.doubleValue() <= op2.doubleValue(); - } else if (op1 instanceof Float || op2 instanceof Float) { + } + else if (op1 instanceof Float || op2 instanceof Float) { return op1.floatValue() <= op2.floatValue(); - } else if (op1 instanceof Long || op2 instanceof Long) { + } + else if (op1 instanceof Long || op2 instanceof Long) { return op1.longValue() <= op2.longValue(); - } else { + } + else { return op1.intValue() <= op2.intValue(); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java index 6618f2cc35e..0739cc68688 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.util.regex.Matcher; @@ -28,8 +29,9 @@ import org.springframework.expression.spel.SpelMessages; /** * Implements the matches operator. Matches takes two operands. The first is a string and the second is a java regex. It * will return true when getValue() is called if the first operand matches the regex. - * + * * @author Andy Clement + * @since 3.0 */ public class OperatorMatches extends Operator { @@ -44,7 +46,6 @@ public class OperatorMatches extends Operator { /** * Check the first operand matches the regex specified as the second operand. - * * @param state the expression state * @return true if the first operand matches the regex specified as the second operand, otherwise false * @throws EvaluationException if there is a problem evaluating the expression (e.g. the regex is invalid) @@ -67,7 +68,8 @@ public class OperatorMatches extends Operator { Pattern pattern = Pattern.compile((String) right); Matcher matcher = pattern.matcher((String) left); return matcher.matches(); - } catch (PatternSyntaxException pse) { + } + catch (PatternSyntaxException pse) { throw new SpelException(rightOp.getCharPositionInLine(), pse, SpelMessages.INVALID_PATTERN, right); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMinus.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMinus.java index 6366590b0aa..f9a14e389dc 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMinus.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMinus.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -24,8 +25,9 @@ import org.springframework.expression.spel.SpelMessages; /** * Implements the minus operator. If there is only one operand it is a unary minus. - * + * * @author Andy Clement + * @since 3.0 */ public class OperatorMinus extends Operator { @@ -55,41 +57,41 @@ public class OperatorMinus extends Operator { if (left instanceof Number) { Number n = (Number) left; if (left instanceof Double) { - Double result = 0 - n.doubleValue(); - return result; - } else if (left instanceof Float) { - Float result = 0 - n.floatValue(); - return result; - } else if (left instanceof Long) { - Long result = 0 - n.longValue(); - return result; - } else { - Integer result = 0 - n.intValue(); - return result; + return 0 - n.doubleValue(); + } + else if (left instanceof Float) { + return 0 - n.floatValue(); + } + else if (left instanceof Long) { + return 0 - n.longValue(); + } + else { + return 0 - n.intValue(); } } throw new SpelException(SpelMessages.CANNOT_NEGATE_TYPE, left.getClass().getName()); - } else { + } + else { Object left = leftOp.getValueInternal(state); Object right = rightOp.getValueInternal(state); if (left instanceof Number && right instanceof Number) { Number op1 = (Number) left; Number op2 = (Number) right; if (op1 instanceof Double || op2 instanceof Double) { - Double result = op1.doubleValue() - op2.doubleValue(); - return result; - } else if (op1 instanceof Float || op2 instanceof Float) { - Float result = op1.floatValue() - op2.floatValue(); - return result; - } else if (op1 instanceof Long || op2 instanceof Long) { - Long result = op1.longValue() - op2.longValue(); - return result; - } else { - Integer result = op1.intValue() - op2.intValue(); - return result; + return op1.doubleValue() - op2.doubleValue(); + } + else if (op1 instanceof Float || op2 instanceof Float) { + return op1.floatValue() - op2.floatValue(); + } + else if (op1 instanceof Long || op2 instanceof Long) { + return op1.longValue() - op2.longValue(); + } + else { + return op1.intValue() - op2.intValue(); } } return state.operate(Operation.SUBTRACT, left, right); } } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorModulus.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorModulus.java index 070d467d325..c55a75cdcd5 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorModulus.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorModulus.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -22,8 +23,9 @@ import org.springframework.expression.spel.ExpressionState; /** * Implements the modulus operator. - * + * * @author Andy Clement + * @since 3.0 */ public class OperatorModulus extends Operator { @@ -44,17 +46,16 @@ public class OperatorModulus extends Operator { Number op1 = (Number) operandOne; Number op2 = (Number) operandTwo; if (op1 instanceof Double || op2 instanceof Double) { - Double result = op1.doubleValue() % op2.doubleValue(); - return result; - } else if (op1 instanceof Float || op2 instanceof Float) { - Float result = op1.floatValue() % op2.floatValue(); - return result; - } else if (op1 instanceof Long || op2 instanceof Long) { - Long result = op1.longValue() % op2.longValue(); - return result; - } else { - Integer result = op1.intValue() % op2.intValue(); - return result; + return op1.doubleValue() % op2.doubleValue(); + } + else if (op1 instanceof Float || op2 instanceof Float) { + return op1.floatValue() % op2.floatValue(); + } + else if (op1 instanceof Long || op2 instanceof Long) { + return op1.longValue() % op2.longValue(); + } + else { + return op1.intValue() % op2.intValue(); } } return state.operate(Operation.MODULUS, operandOne, operandTwo); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMultiply.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMultiply.java index e7f02acf746..95e32f4de6f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMultiply.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMultiply.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -23,14 +24,15 @@ import org.springframework.expression.spel.ExpressionState; /** * Implements the multiply operator. Conversions and promotions: * http://java.sun.com/docs/books/jls/third_edition/html/conversions.html Section 5.6.2: - * - * If any of the operands is of a reference type, unboxing conversion (¤5.1.8) is performed. Then:
+ * + *

If any of the operands is of a reference type, unboxing conversion (¤5.1.8) is performed. Then:
* If either operand is of type double, the other is converted to double.
* Otherwise, if either operand is of type float, the other is converted to float.
* Otherwise, if either operand is of type long, the other is converted to long.
* Otherwise, both operands are converted to type int. - * + * * @author Andy Clement + * @since 3.0 */ public class OperatorMultiply extends Operator { @@ -55,20 +57,20 @@ public class OperatorMultiply extends Operator { Number op1 = (Number) operandOne; Number op2 = (Number) operandTwo; if (op1 instanceof Double || op2 instanceof Double) { - Double result = op1.doubleValue() * op2.doubleValue(); - return result; - } else if (op1 instanceof Float || op2 instanceof Float) { - Float result = op1.floatValue() * op2.floatValue(); - return result; - } else if (op1 instanceof Long || op2 instanceof Long) { - Long result = op1.longValue() * op2.longValue(); - return result; - } else { // promote to int - Integer result = op1.intValue() * op2.intValue(); - return result; + return op1.doubleValue() * op2.doubleValue(); } - } else if (operandOne instanceof String && operandTwo instanceof Integer) { - int repeats = ((Integer) operandTwo).intValue(); + else if (op1 instanceof Float || op2 instanceof Float) { + return op1.floatValue() * op2.floatValue(); + } + else if (op1 instanceof Long || op2 instanceof Long) { + return op1.longValue() * op2.longValue(); + } + else { + return op1.intValue() * op2.intValue(); + } + } + else if (operandOne instanceof String && operandTwo instanceof Integer) { + int repeats = (Integer) operandTwo; StringBuilder result = new StringBuilder(); for (int i = 0; i < repeats; i++) { result.append(operandOne); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java index aa5d1f35e57..9534a245d22 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -20,6 +21,12 @@ import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.ExpressionState; +/** + * Represents a NOT operation. + * + * @author Andy Clement + * @since 3.0 + */ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do not extend BinaryOperator public OperatorNot(Token payload) { @@ -29,9 +36,10 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do @Override public Object getValueInternal(ExpressionState state) throws EvaluationException { try { - boolean value = state.toBoolean(getChild(0).getValueInternal(state)); + boolean value = state.convertValue(getChild(0).getValueInternal(state), Boolean.class); return !value; - } catch (SpelException see) { + } + catch (SpelException see) { see.setPosition(getChild(0).getCharPositionInLine()); throw see; } @@ -48,4 +56,5 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do public boolean isWritable(ExpressionState expressionState) throws SpelException { return false; } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorOr.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorOr.java index fc30fa94f9e..7825f903205 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorOr.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorOr.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -22,8 +23,9 @@ import org.springframework.expression.spel.ExpressionState; /** * Represents the boolean OR operation. - * + * * @author Andy Clement + * @since 3.0 */ public class OperatorOr extends Operator { @@ -41,18 +43,21 @@ public class OperatorOr extends Operator { boolean leftValue; boolean rightValue; try { - leftValue = state.toBoolean(getLeftOperand().getValueInternal(state)); - } catch (SpelException see) { + leftValue = state.convertValue(getLeftOperand().getValueInternal(state), Boolean.class); + } + catch (SpelException see) { see.setPosition(getLeftOperand().getCharPositionInLine()); throw see; } - if (leftValue == true) + if (leftValue == true) { return true; // no need to evaluate right operand + } try { - rightValue = state.toBoolean(getRightOperand().getValueInternal(state)); - } catch (SpelException see) { + rightValue = state.convertValue(getRightOperand().getValueInternal(state), Boolean.class); + } + catch (SpelException see) { see.setPosition(getRightOperand().getCharPositionInLine()); throw see; } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java index 87a50914fd4..4c9b5b67164 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; + import org.springframework.expression.EvaluationException; import org.springframework.expression.Operation; import org.springframework.expression.spel.ExpressionState; +/** + * @author Andy Clement + * @since 3.0 + */ public class OperatorPlus extends Operator { public OperatorPlus(Token payload) { @@ -33,41 +39,40 @@ public class OperatorPlus extends Operator { if (rightOp == null) { // If only one operand, then this is unary plus Object operandOne = leftOp.getValueInternal(state); if (operandOne instanceof Number) { - return new Integer(((Number) operandOne).intValue()); + return ((Number) operandOne).intValue(); } return state.operate(Operation.ADD, operandOne, null); - } else { + } + else { Object operandOne = leftOp.getValueInternal(state); Object operandTwo = rightOp.getValueInternal(state); if (operandOne instanceof Number && operandTwo instanceof Number) { Number op1 = (Number) operandOne; Number op2 = (Number) operandTwo; if (op1 instanceof Double || op2 instanceof Double) { - Double result = op1.doubleValue() + op2.doubleValue(); - return result; - } else if (op1 instanceof Float || op2 instanceof Float) { - Float result = op1.floatValue() + op2.floatValue(); - return result; - } else if (op1 instanceof Long || op2 instanceof Long) { - Long result = op1.longValue() + op2.longValue(); - return result; - } else { // TODO what about overflow? - Integer result = op1.intValue() + op2.intValue(); - return result; + return op1.doubleValue() + op2.doubleValue(); } - } else if (operandOne instanceof String && operandTwo instanceof String) { + else if (op1 instanceof Float || op2 instanceof Float) { + return op1.floatValue() + op2.floatValue(); + } + else if (op1 instanceof Long || op2 instanceof Long) { + return op1.longValue() + op2.longValue(); + } + else { // TODO what about overflow? + return op1.intValue() + op2.intValue(); + } + } + else if (operandOne instanceof String && operandTwo instanceof String) { return new StringBuilder((String) operandOne).append((String) operandTwo).toString(); - } else if (operandOne instanceof String && operandTwo instanceof Integer) { + } + else if (operandOne instanceof String && operandTwo instanceof Integer) { String l = (String) operandOne; Integer i = (Integer) operandTwo; - // implements character + int (ie. a + 1 = b) if (l.length() == 1) { - Character c = new Character((char) (new Character(l.charAt(0)) + i)); - return c.toString(); + return Character.toString((char) (l.charAt(0) + i)); } - - return new StringBuilder((String) operandOne).append(((Integer) operandTwo).toString()).toString(); + return new StringBuilder(l).append(i).toString(); } return state.operate(Operation.ADD, operandOne, operandTwo); } @@ -80,9 +85,10 @@ public class OperatorPlus extends Operator { @Override public String toStringAST() { - if (getRightOperand() == null) { // unary plus + if (getRightOperand() == null) { // unary plus return new StringBuilder().append("+").append(getLeftOperand()).toString(); } return super.toStringAST(); } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java index a03347ed3f0..d67b5058a23 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Placeholder.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -25,7 +26,7 @@ import org.springframework.expression.spel.ExpressionState; * information for messages/etc. * * @author Andy Clement - * + * @since 3.0 */ public class Placeholder extends SpelNodeImpl { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java index ef7d8a74179..cf7614514d9 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.util.ArrayList; @@ -21,11 +22,11 @@ import java.util.List; import java.util.Map; import org.antlr.runtime.Token; + import org.springframework.expression.EvaluationException; +import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.internal.KeyValuePair; /** * Represents projection, where a given operation is performed on all elements in some input sequence, returning diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index 3f415be72ca..3e7654d181c 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,84 +13,87 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.util.ArrayList; import java.util.List; import org.antlr.runtime.Token; + import org.springframework.expression.AccessException; -import org.springframework.expression.CacheablePropertyAccessor; import org.springframework.expression.EvaluationContext; import org.springframework.expression.PropertyAccessor; -import org.springframework.expression.PropertyReaderExecutor; -import org.springframework.expression.PropertyWriterExecutor; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.internal.Utils; /** * Represents a simple property or field reference. * * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 */ public class PropertyOrFieldReference extends SpelNodeImpl { - public static boolean useCaching = true; + private final String name; + + private volatile PropertyAccessor cachedReadAccessor; + + private volatile PropertyAccessor cachedWriteAccessor; - private final Object name; - private PropertyReaderExecutor cachedReaderExecutor; - private PropertyWriterExecutor cachedWriterExecutor; public PropertyOrFieldReference(Token payload) { super(payload); - name = payload.getText(); + this.name = payload.getText(); } + @Override public Object getValueInternal(ExpressionState state) throws SpelException { - return readProperty(state, name); + return readProperty(state, this.name); } @Override public void setValue(ExpressionState state, Object newValue) throws SpelException { - writeProperty(state, name, newValue); + writeProperty(state, this.name, newValue); } @Override public boolean isWritable(ExpressionState state) throws SpelException { - return isWritableProperty(name, state); + return isWritableProperty(this.name, state); } @Override public String toStringAST() { - return name.toString(); + return this.name; } /** * Attempt to read the named property from the current context object. - * * @param state the evaluation state * @param name the name of the property * @return the value of the property * @throws SpelException if any problem accessing the property or it cannot be found */ - private Object readProperty(ExpressionState state, Object name) throws SpelException { + private Object readProperty(ExpressionState state, String name) throws SpelException { Object contextObject = state.getActiveContextObject(); EvaluationContext eContext = state.getEvaluationContext(); - if (cachedReaderExecutor != null) { + PropertyAccessor accessorToUse = this.cachedReadAccessor; + if (accessorToUse != null) { try { - return cachedReaderExecutor.execute(state.getEvaluationContext(), contextObject); - } catch (AccessException ae) { + return accessorToUse.read(state.getEvaluationContext(), contextObject, name); + } + catch (AccessException ae) { // this is OK - it may have gone stale due to a class change, // let's try to get a new one and call it before giving up + this.cachedReadAccessor = null; } } Class contextObjectClass = getObjectClass(contextObject); - List accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state); // Go through the accessors that may be able to resolve it. If they are a cacheable accessor then @@ -99,47 +102,34 @@ public class PropertyOrFieldReference extends SpelNodeImpl { if (accessorsToTry != null) { try { for (PropertyAccessor accessor : accessorsToTry) { - if (accessor instanceof CacheablePropertyAccessor) { - cachedReaderExecutor = ((CacheablePropertyAccessor) accessor).getReaderAccessor(eContext, - contextObject, name); - if (cachedReaderExecutor != null) { - try { - return cachedReaderExecutor.execute(state.getEvaluationContext(), contextObject); - } catch (AccessException ae) { - cachedReaderExecutor = null; - throw ae; - } finally { - if (!useCaching) { - cachedReaderExecutor = null; - } - } - } - } else { - if (accessor.canRead(eContext, contextObject, name)) { - Object value = accessor.read(eContext, contextObject, name); - return value; - } + if (accessor.canRead(eContext, contextObject, name)) { + this.cachedReadAccessor = accessor; + return accessor.read(eContext, contextObject, name); } } - } catch (AccessException ae) { + } + catch (AccessException ae) { throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage()); } } - throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, name, Utils - .formatClassnameForMessage(contextObjectClass)); + throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, name, + FormatHelper.formatClassNameForMessage(contextObjectClass)); } - private void writeProperty(ExpressionState state, Object name, Object newValue) throws SpelException { + private void writeProperty(ExpressionState state, String name, Object newValue) throws SpelException { Object contextObject = state.getActiveContextObject(); EvaluationContext eContext = state.getEvaluationContext(); - if (cachedWriterExecutor != null) { + PropertyAccessor accessorToUse = this.cachedWriteAccessor; + if (accessorToUse != null) { try { - cachedWriterExecutor.execute(state.getEvaluationContext(), contextObject, newValue); + accessorToUse.write(state.getEvaluationContext(), contextObject, name, newValue); return; - } catch (AccessException ae) { + } + catch (AccessException ae) { // this is OK - it may have gone stale due to a class change, // let's try to get a new one and call it before giving up + this.cachedWriteAccessor = null; } } @@ -149,27 +139,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl { if (accessorsToTry != null) { try { for (PropertyAccessor accessor : accessorsToTry) { - if (accessor instanceof CacheablePropertyAccessor) { - cachedWriterExecutor = ((CacheablePropertyAccessor) accessor).getWriterAccessor(eContext, - contextObject, name); - if (cachedWriterExecutor != null) { - try { - cachedWriterExecutor.execute(state.getEvaluationContext(), contextObject, newValue); - return; - } catch (AccessException ae) { - cachedWriterExecutor = null; - throw ae; - } finally { - if (!useCaching) { - cachedWriterExecutor = null; - } - } - } - } else { - if (accessor.canWrite(eContext, contextObject, name)) { - accessor.write(eContext, contextObject, name, newValue); - return; - } + if (accessor.canWrite(eContext, contextObject, name)) { + this.cachedWriteAccessor = accessor; + accessor.write(eContext, contextObject, name, newValue); + return; } } } catch (AccessException ae) { @@ -177,11 +150,11 @@ public class PropertyOrFieldReference extends SpelNodeImpl { name, ae.getMessage()); } } - throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND, name, Utils - .formatClassnameForMessage(contextObjectClass)); + throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND, name, FormatHelper + .formatClassNameForMessage(contextObjectClass)); } - public boolean isWritableProperty(Object name, ExpressionState state) throws SpelException { + public boolean isWritableProperty(String name, ExpressionState state) throws SpelException { Object contextObject = state.getActiveContextObject(); EvaluationContext eContext = state.getEvaluationContext(); if (contextObject == null) { @@ -210,7 +183,6 @@ public class PropertyOrFieldReference extends SpelNodeImpl { * the start of the list. In addition, there are specific resolvers that exactly name the class in question and * resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the * specific resolvers set and will be tried after exactly matching accessors but before generic accessors. - * * @param targetType the type upon which property access is being attempted * @return a list of resolvers that should be tried in order to access the property */ @@ -221,15 +193,16 @@ public class PropertyOrFieldReference extends SpelNodeImpl { Class[] targets = resolver.getSpecificTargetClasses(); if (targets == null) { // generic resolver that says it can be used for any type generalAccessors.add(resolver); - } else { + } + else { if (targetType != null) { int pos = 0; - for (int i = 0; i < targets.length; i++) { - Class clazz = targets[i]; + for (Class clazz : targets) { if (clazz == targetType) { // put exact matches on the front to be tried first? specificAccessors.add(pos++, resolver); - } else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the - // specificAccessor list + } + else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the + // specificAccessor list generalAccessors.add(resolver); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java index 19cbba945e0..a15ac078e3b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,19 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; + import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; /** * Represents a dot separated sequence of strings that indicate a package qualified type reference. - *

- * Example: "java.lang.String" as in the expression "new java.lang.String('hello')" - * + * + *

Example: "java.lang.String" as in the expression "new java.lang.String('hello')" + * * @author Andy Clement - * + * @since 3.0 */ public class QualifiedIdentifier extends SpelNodeImpl { @@ -39,27 +41,30 @@ public class QualifiedIdentifier extends SpelNodeImpl { @Override public Object getValueInternal(ExpressionState state) throws EvaluationException { // Cache the concatenation of child identifiers - if (value == null) { + if (this.value == null) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < getChildCount(); i++) { - if (i > 0) + if (i > 0) { sb.append("."); + } sb.append(getChild(i).getValueInternal(state)); } - value = sb.toString(); + this.value = sb.toString(); } - return value; + return this.value; } @Override public String toStringAST() { StringBuilder sb = new StringBuilder(); - if (value != null) { - sb.append(value); - } else { + if (this.value != null) { + sb.append(this.value); + } + else { for (int i = 0; i < getChildCount(); i++) { - if (i > 0) + if (i > 0) { sb.append("."); + } sb.append(getChild(i).toStringAST()); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java index b4fcb2bf3d9..17461e2e30d 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; +/** + * @author Andy Clement + * @since 3.0 + */ public class RealLiteral extends Literal { private final Double value; @@ -28,7 +33,7 @@ public class RealLiteral extends Literal { @Override public Double getLiteralValue() { - return value; + return this.value; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java index e431eaa8d98..94ddce2355f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.util.ArrayList; @@ -21,11 +22,11 @@ import java.util.List; import java.util.Map; import org.antlr.runtime.Token; + import org.springframework.expression.EvaluationException; +import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.internal.KeyValuePair; /** * Represents selection over a map or collection. For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java index db211416f68..43fcefc3fad 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,32 +13,33 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import java.io.Serializable; import org.antlr.runtime.Token; import org.antlr.runtime.tree.CommonTree; + import org.springframework.expression.EvaluationException; import org.springframework.expression.common.ExpressionUtils; import org.springframework.expression.spel.ExpressionState; -import org.springframework.expression.spel.SpelNode; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; +import org.springframework.expression.spel.SpelNode; import org.springframework.expression.spel.generated.SpringExpressionsParser; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * The common supertype of all AST nodes in a parsed Spring Expression Language format expression. - * + * * @author Andy Clement - * + * @since 3.0 */ -public abstract class SpelNodeImpl extends CommonTree implements Serializable, SpelNode { +public abstract class SpelNodeImpl extends CommonTree implements SpelNode, Serializable { /** * The Antlr parser uses this constructor to build SpelNodes. - * * @param payload the token for the node that has been parsed */ protected SpelNodeImpl(Token payload) { @@ -46,38 +47,23 @@ public abstract class SpelNodeImpl extends CommonTree implements Serializable, S } public final Object getValue(ExpressionState expressionState) throws EvaluationException { - if (expressionState==null) { - return getValue(new ExpressionState(new StandardEvaluationContext())); - } else { + if (expressionState != null) { return getValueInternal(expressionState); } + else { + return getValue(new ExpressionState(new StandardEvaluationContext())); + } } - - - /* (non-Javadoc) - * @see org.springframework.expression.spel.ast.ISpelNode#getValue(org.springframework.expression.spel.ExpressionState) - */ - public abstract Object getValueInternal(ExpressionState expressionState) throws EvaluationException; - - /* (non-Javadoc) - * @see org.springframework.expression.spel.ast.ISpelNode#isWritable(org.springframework.expression.spel.ExpressionState) - */ public boolean isWritable(ExpressionState expressionState) throws EvaluationException { return false; } - /* (non-Javadoc) - * @see org.springframework.expression.spel.ast.ISpelNode#setValue(org.springframework.expression.spel.ExpressionState, java.lang.Object) - */ public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException { - throw new SpelException(getCharPositionInLine(), SpelMessages.SETVALUE_NOT_SUPPORTED, getClass(), - getTokenName()); + throw new SpelException( + getCharPositionInLine(), SpelMessages.SETVALUE_NOT_SUPPORTED, getClass(), getTokenName()); } - /** - * @return return the token this node represents - */ protected String getTokenName() { if (getToken() == null) { return "UNKNOWN"; @@ -85,42 +71,39 @@ public abstract class SpelNodeImpl extends CommonTree implements Serializable, S return SpringExpressionsParser.tokenNames[getToken().getType()]; } - /* (non-Javadoc) - * @see org.springframework.expression.spel.ast.ISpelNode#toStringAST() - */ - public abstract String toStringAST(); - - /* (non-Javadoc) - * @see org.springframework.expression.spel.ast.ISpelNode#getChild(int) - */ @Override public SpelNodeImpl getChild(int index) { return (SpelNodeImpl) super.getChild(index); } - /* (non-Javadoc) - * @see org.springframework.expression.spel.ast.ISpelNode#getObjectClass(java.lang.Object) - */ - public Class getObjectClass(Object o) { - if (o == null) + public Class getObjectClass(Object obj) { + if (obj == null) { return null; - return (o instanceof Class) ? ((Class) o) : o.getClass(); + } + return (obj instanceof Class ? ((Class) obj) : obj.getClass()); } - protected final Object getValue(ExpressionState state, Class desiredReturnType) throws EvaluationException { + @SuppressWarnings("unchecked") + protected final T getValue(ExpressionState state, Class desiredReturnType) throws EvaluationException { Object result = getValueInternal(state); if (result != null && desiredReturnType != null) { Class resultType = result.getClass(); if (desiredReturnType.isAssignableFrom(resultType)) { - return result; + return (T) result; } // Attempt conversion to the requested type, may throw an exception return ExpressionUtils.convert(state.getEvaluationContext(), result, desiredReturnType); } - return result; + return (T) result; } public int getStartPosition() { return getCharPositionInLine(); } + + + public abstract Object getValueInternal(ExpressionState expressionState) throws EvaluationException; + + public abstract String toStringAST(); + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java index 42866b1f68d..74c3ef5714e 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/SpelTreeAdaptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; import org.antlr.runtime.tree.CommonTreeAdaptor; + import org.springframework.expression.spel.generated.SpringExpressionsLexer; +/** + * @author Andy Clement + * @since 3.0 + */ public class SpelTreeAdaptor extends CommonTreeAdaptor { + @Override public Object create(Token payload) { if (payload != null) { @@ -131,4 +138,5 @@ public class SpelTreeAdaptor extends CommonTreeAdaptor { } return new EmptySpelNode(payload); } -} \ No newline at end of file + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java index 70159878895..edde66c9c3a 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,30 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; +/** + * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 + */ public class StringLiteral extends Literal { - private String value; + private final String value; public StringLiteral(Token payload) { super(payload); - value = payload.getText(); + String val = payload.getText(); // TODO should these have been skipped being created by the parser rules? or not? - value = value.substring(1, value.length() - 1); - value = value.replaceAll("''", "'"); + val = val.substring(1, val.length() - 1); + this.value = val.replaceAll("''", "'"); } @Override public String getLiteralValue() { - return value; + return this.value; } @Override public String toString() { - return new StringBuilder("'").append(getLiteralValue()).append("'").toString(); + return "'" + getLiteralValue() + "'"; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java index bc9c4f4cefb..9ae1c3d501a 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; @@ -22,8 +23,10 @@ import org.springframework.expression.spel.SpelException; /** * Represents a ternary expression, for example: "someCheck()?true:false". - * + * * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 */ public class Ternary extends SpelNodeImpl { @@ -33,21 +36,22 @@ public class Ternary extends SpelNodeImpl { /** * Evaluate the condition and if true evaluate the first alternative, otherwise evaluate the second alternative. - * * @param state the expression state * @throws EvaluationException if the condition does not evaluate correctly to a boolean or there is a problem * executing the chosen alternative */ @Override public Object getValueInternal(ExpressionState state) throws EvaluationException { - Boolean b = (Boolean) getChild(0).getValue(state, Boolean.class); + Boolean b = getChild(0).getValue(state, Boolean.class); try { - if (b) { + if (b != null && b.booleanValue()) { return getChild(1).getValueInternal(state); - } else { + } + else { return getChild(2).getValueInternal(state); } - } catch (SpelException ex) { + } + catch (SpelException ex) { ex.setPosition(getChild(0).getCharPositionInLine()); throw ex; } @@ -63,4 +67,5 @@ public class Ternary extends SpelNodeImpl { public boolean isWritable(ExpressionState expressionState) throws SpelException { return false; } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/TypeCode.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java similarity index 91% rename from org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/TypeCode.java rename to org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java index 0333b5cc899..12454e93d48 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/TypeCode.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.spel.internal; -public enum TypeCode { +package org.springframework.expression.spel.ast; + +enum TypeCode { OBJECT(0, Object.class), BOOLEAN(1, Boolean.TYPE), BYTE(1, Byte.TYPE), CHAR(1, Character.TYPE), SHORT(2, Short.TYPE), INT( 3, Integer.TYPE), LONG(4, Long.TYPE), FLOAT(5, Float.TYPE), DOUBLE(6, Double.TYPE); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java index 6877e970eeb..2ac2e22d8e8 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; + import org.springframework.expression.EvaluationException; import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; -import org.springframework.expression.spel.internal.TypeCode; /** * Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)" * * @author Andy Clement - * */ public class TypeReference extends SpelNodeImpl { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java index 6d9c12a833c..f40302b70e6 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/VariableReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,58 +13,64 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; + +import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.ExpressionState; /** * Represents a variable reference, eg. #someVar. Note this is different to a *local* variable like $someVar * * @author Andy Clement - * + * @since 3.0 */ public class VariableReference extends SpelNodeImpl { // Well known variables: - private final static String THIS = "this"; // currently active context object - private final static String ROOT = "root"; // root context object + private final static String THIS = "this"; // currently active context object + private final static String ROOT = "root"; // root context object private final String name; + public VariableReference(Token payload) { super(payload); - name = payload.getText(); + this.name = payload.getText(); } + @Override public Object getValueInternal(ExpressionState state) throws SpelException { - if (name.equals(THIS)) + if (this.name.equals(THIS)) { return state.getActiveContextObject(); - if (name.equals(ROOT)) + } + if (this.name.equals(ROOT)) { return state.getRootContextObject(); - Object result = state.lookupVariable(name); + } + Object result = state.lookupVariable(this.name); if (result == null) { - throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, name); + throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, this.name); } return result; } @Override public void setValue(ExpressionState state, Object value) throws SpelException { - // Object oldValue = state.lookupVariable(name); - state.setVariable(name, value); + state.setVariable(this.name, value); } @Override public String toStringAST() { - return new StringBuilder("#").append(name).toString(); + return "#" + this.name; } @Override public boolean isWritable(ExpressionState expressionState) throws SpelException { - return !(name.equals(THIS) || name.equals(ROOT)); + return !(this.name.equals(THIS) || this.name.equals(ROOT)); } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/WrappedExpressionException.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/WrappedExpressionException.java deleted file mode 100644 index 6f0dac55b17..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/internal/WrappedExpressionException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.internal; - -import org.springframework.expression.spel.SpelException; - -/** - * Wraps an ELException and can pass up through Antlr since it is unchecked, where it can then be unwrapped. - * - * @author Andy Clement - */ -public class WrappedExpressionException extends RuntimeException { - - WrappedExpressionException(SpelException e) { - super(e); - } - - @Override - public SpelException getCause() { - return (SpelException) super.getCause(); - } -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorExecutor.java deleted file mode 100644 index e83123720ce..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorExecutor.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.reflection; - -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; - -import org.springframework.expression.AccessException; -import org.springframework.expression.ConstructorExecutor; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.EvaluationException; - -/** - * A simple CommandExecutor implementation that runs a constructor using reflective invocation. - * - * @author Andy Clement - */ -public class ReflectionConstructorExecutor implements ConstructorExecutor { - - private final Constructor c; - - // When the constructor was found, we will have determined if arguments need to be converted for it - // to be invoked. Conversion won't be cheap so let's only do it if necessary. - private final Integer[] argsRequiringConversion; - - public ReflectionConstructorExecutor(Constructor constructor, Integer[] argsRequiringConversion) { - c = constructor; - this.argsRequiringConversion = argsRequiringConversion; - } - - /** - * Invoke a constructor via reflection. - */ - public Object execute(EvaluationContext context, Object... arguments) throws AccessException { - if (argsRequiringConversion != null && arguments != null) { - try { - ReflectionUtils.convertArguments(c.getParameterTypes(), c.isVarArgs(), context.getTypeUtils() - .getTypeConverter(), argsRequiringConversion, arguments); - } catch (EvaluationException ex) { - throw new AccessException("Problem invoking constructor on '" + c + "': " + ex.getMessage(), ex); - } - } - if (c.isVarArgs()) { - arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(c.getParameterTypes(), arguments); - } - try { - if (!c.isAccessible()) { - c.setAccessible(true); - } - return c.newInstance(arguments); - } catch (IllegalArgumentException e) { - throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e); - } catch (InstantiationException e) { - throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e); - } catch (IllegalAccessException e) { - throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e); - } catch (InvocationTargetException e) { - throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e); - } - } -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorResolver.java deleted file mode 100644 index c96b4a4eeb9..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorResolver.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.reflection; - -import org.springframework.expression.AccessException; -import org.springframework.expression.ConstructorExecutor; -import org.springframework.expression.ConstructorResolver; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.EvaluationException; -import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredConstructor; - -/** - * A constructor resolver that uses reflection to locate the constructor that should be invoked - * - * @author Andy Clement - */ -public class ReflectionConstructorResolver implements ConstructorResolver { - - /* - * Indicates if this resolve will allow matches to be found that require some of the input arguments to be - * transformed by the conversion service. - */ - private boolean allowMatchesRequiringArgumentConversion = true; - - public ReflectionConstructorResolver() { - } - - public ReflectionConstructorResolver(boolean allowMatchesRequiringArgumentConversion) { - this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion; - } - - public void setAllowMatchRequiringArgumentConversion(boolean allow) { - this.allowMatchesRequiringArgumentConversion = allow; - } - - /** - * Locate a matching constructor or return null if non can be found. - */ - public ConstructorExecutor resolve(EvaluationContext context, String typename, Class[] argumentTypes) - throws AccessException { - try { - Class c = context.getTypeUtils().getTypeLocator().findType(typename); - DiscoveredConstructor dCtor = ReflectionUtils.findConstructor(context.getTypeUtils().getTypeConverter(), c, - argumentTypes, allowMatchesRequiringArgumentConversion); - if (dCtor == null) { - return null; - } - return new ReflectionConstructorExecutor(dCtor.theConstructor, dCtor.argumentsRequiringConversion); - } catch (EvaluationException e) { - throw new AccessException(null,e); - } - } -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodExecutor.java deleted file mode 100644 index 7a6e6be8101..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodExecutor.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.reflection; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.EvaluationException; -import org.springframework.expression.MethodExecutor; - -public class ReflectionMethodExecutor implements MethodExecutor { - - private final Method m; - - // When the method was found, we will have determined if arguments need to be converted for it - // to be invoked. Conversion won't be cheap so let's only do it if necessary. - private final Integer[] argsRequiringConversion; - - public ReflectionMethodExecutor(Method theMethod, Integer[] argumentsRequiringConversion) { - m = theMethod; - argsRequiringConversion = argumentsRequiringConversion; - } - - public Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { - if (argsRequiringConversion != null && arguments != null) { - try { - ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), context.getTypeUtils() - .getTypeConverter(), argsRequiringConversion, arguments); - } catch (EvaluationException ex) { - throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() - + "': " + ex.getMessage(), ex); - } - } - if (m.isVarArgs()) { - arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), arguments); - } - try { - if (!m.isAccessible()) { - m.setAccessible(true); - } - return m.invoke(target, arguments); - } catch (IllegalArgumentException e) { - throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': " - + e.getMessage(), e); - } catch (IllegalAccessException e) { - throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': " - + e.getMessage(), e); - } catch (InvocationTargetException e) { - e.getCause().printStackTrace(); - throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': " - + e.getMessage(), e); - } - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodResolver.java deleted file mode 100644 index 572a1b97e02..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodResolver.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.reflection; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.EvaluationException; -import org.springframework.expression.MethodExecutor; -import org.springframework.expression.MethodResolver; -import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredMethod; - -/** - * A method resolver that uses reflection to locate the method that should be invoked - * - * @author Andy Clement - */ -public class ReflectionMethodResolver implements MethodResolver { - - /* - * Indicates if this resolve will allow matches to be found that require some of the input arguments to be - * transformed by the conversion service. - */ - private boolean allowMatchesRequiringArgumentConversion = true; - - public ReflectionMethodResolver() { - } - - public ReflectionMethodResolver(boolean allowMatchesRequiringArgumentConversion) { - this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion; - } - - public void setAllowMatchRequiringArgumentConversion(boolean allow) { - this.allowMatchesRequiringArgumentConversion = allow; - } - - public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class[] argumentTypes) throws AccessException { - try { - Class relevantClass = (targetObject instanceof Class ? (Class) targetObject : targetObject.getClass()); - DiscoveredMethod dMethod = ReflectionUtils.findMethod(context.getTypeUtils().getTypeConverter(), name, - argumentTypes, relevantClass, allowMatchesRequiringArgumentConversion); - if (dMethod == null) { - return null; - } - return new ReflectionMethodExecutor(dMethod.theMethod, dMethod.argumentsRequiringConversion); - } catch (EvaluationException e) { - throw new AccessException(null,e); - } - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutor.java deleted file mode 100644 index a4cf698f0a0..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutor.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.springframework.expression.spel.reflection; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.PropertyReaderExecutor; - -public class ReflectionPropertyReaderExecutor implements PropertyReaderExecutor { - - private Method methodToAccessProperty; - private Field fieldToAccessProperty; - private final String propertyName; - - public ReflectionPropertyReaderExecutor(String propertyName, Method method) { - this.propertyName = propertyName; - methodToAccessProperty = method; - } - - public ReflectionPropertyReaderExecutor(String propertyName, Field field) { - this.propertyName = propertyName; - fieldToAccessProperty = field; - } - - public Object execute(EvaluationContext context, Object target) throws AccessException { - if (methodToAccessProperty != null) { - try { - if (!methodToAccessProperty.isAccessible()) { - methodToAccessProperty.setAccessible(true); - } - return methodToAccessProperty.invoke(target); - } catch (IllegalArgumentException e) { - throw new AccessException("Unable to access property '" + propertyName + "' through getter", e); - } catch (IllegalAccessException e) { - throw new AccessException("Unable to access property '" + propertyName + "' through getter", e); - } catch (InvocationTargetException e) { - throw new AccessException("Unable to access property '" + propertyName + "' through getter", e); - } - } - if (fieldToAccessProperty != null) { - try { - if (!fieldToAccessProperty.isAccessible()) { - fieldToAccessProperty.setAccessible(true); - } - return fieldToAccessProperty.get(target); - } catch (IllegalArgumentException e) { - throw new AccessException("Unable to access field: " + propertyName, e); - } catch (IllegalAccessException e) { - throw new AccessException("Unable to access field: " + propertyName, e); - } - } - throw new AccessException("No method or field accessor found for property '" + propertyName + "'"); - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutorForArrayLength.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutorForArrayLength.java deleted file mode 100644 index ade80cc4058..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutorForArrayLength.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.springframework.expression.spel.reflection; - -import java.lang.reflect.Array; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.PropertyReaderExecutor; - -public class ReflectionPropertyReaderExecutorForArrayLength implements PropertyReaderExecutor { - - public ReflectionPropertyReaderExecutorForArrayLength() { - } - - public Object execute(EvaluationContext context, Object target) throws AccessException { - if (target.getClass().isArray()) { - return Array.getLength(target); - } - throw new AccessException("Cannot determine length of a non-array type '" + target.getClass() + "'"); - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyResolver.java deleted file mode 100644 index 0f2f6d69c9d..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyResolver.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.reflection; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import org.springframework.expression.CacheablePropertyAccessor; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.PropertyReaderExecutor; -import org.springframework.expression.PropertyWriterExecutor; - -/** - * Simple PropertyResolver that uses reflection to access properties for reading and writing. A property can be accessed - * if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written). This - * implementation currently follows the Resolver/Executor model (it extends CacheablePropertyAccessor) - the code that - * would be used if it were a simple property accessor is shown at the end. - * - * @author Andy Clement - */ -public class ReflectionPropertyResolver extends CacheablePropertyAccessor { - - /** - * @return null which means this is a general purpose accessor - */ - public Class[] getSpecificTargetClasses() { - return null; - } - - /** - * Use reflection to discover if a named property is accessible on an target type and if it is return an executor - * object that can be called repeatedly to retrieve that property. A property is accessible either as a field or - * through a getter. - * - * @param context the context in which the access is being attempted - * @param target the target object on which the property is being accessed - * @param name the name of the property - */ - @Override - public PropertyReaderExecutor getReaderAccessor(EvaluationContext context, Object target, Object name) { - if (target == null) { - return null; - } - Class relevantClass = (target instanceof Class ? (Class) target : target.getClass()); - if (!(name instanceof String)) { - // A property not found exception will occur if the reflection finder was supposed to find it - return null; - } - String propertyName = (String) name; - if (relevantClass.isArray() && propertyName.equals("length")) { - return new ReflectionPropertyReaderExecutorForArrayLength(); - } - Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass, target instanceof Class); - if (m != null) { - return new ReflectionPropertyReaderExecutor(propertyName, m); - } - Field field = ReflectionUtils.findField(propertyName, relevantClass, target instanceof Class); - if (field != null) { - return new ReflectionPropertyReaderExecutor(propertyName, field); - } - return null; - } - - /** - * Use reflection to discover if a named property is accessible on an target type and if it is return an executor - * object that can be called repeatedly to set that property. A property is writable either as a field or through a - * setter. - * - * @param context the context in which the set is being attempted - * @param target the target object on which the property is being set - * @param name the name of the property - */ - @Override - public PropertyWriterExecutor getWriterAccessor(EvaluationContext context, Object target, Object name) { - if (target == null) { - return null; - } - Class relevantClass = (target instanceof Class ? (Class) target : target.getClass()); - if (!(name instanceof String)) { - // A property not found exception will occur if the reflection finder was supposed to find it - return null; - } - Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass, target instanceof Class); - if (m != null) { - return new ReflectionPropertyWriterExecutor((String) name, m); - } - Field field = ReflectionUtils.findField((String) name, relevantClass, target instanceof Class); - if (field != null) { - return new ReflectionPropertyWriterExecutor((String) name, field); - } - return null; - } - - // /** - // * Return true if the resolver is able to read the specified property from the specified target. - // */ - // public boolean canRead(EvaluationContext relatedContext, Object target, Object name) throws AccessException { - // if (target==null) { - // return false; - // } - // Class relevantClass = (target instanceof Class ? (Class) target : target.getClass()); - // if (!(name instanceof String)) { - // return false; - // } - // String propertyName = (String) name; - // Field field = ReflectionUtils.findField(propertyName, relevantClass); - // if (field != null) { - // return true; - // } - // Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass); - // if (m != null) { - // return true; - // } - // return false; - // } - // - // /** - // * Read the specified property from the specified target. // - // */ - // public Object read(EvaluationContext context, Object target, Object name) throws AccessException { - // if (target==null) { - // return null; - // } - // Class relevantClass = (target instanceof Class ? (Class) target : target.getClass()); - // if (!(name instanceof String)) { - // return null; - // } - // String propertyName = (String) name; - // Field field = ReflectionUtils.findField(propertyName, relevantClass); - // if (field != null) { - // try { - // if (!field.isAccessible()) { - // field.setAccessible(true); - // } - // return field.get(target); - // } catch (IllegalArgumentException e) { - // throw new AccessException("Unable to access field: " + name, e); - // } catch (IllegalAccessException e) { - // throw new AccessException("Unable to access field: " + name, e); - // } - // } - // Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass); - // if (m != null) { - // try { - // if (!m.isAccessible()) - // m.setAccessible(true); - // return m.invoke(target); - // } catch (IllegalArgumentException e) { - // throw new AccessException("Unable to access property '" + name + "' through getter", e); - // } catch (IllegalAccessException e) { - // throw new AccessException("Unable to access property '" + name + "' through getter", e); - // } catch (InvocationTargetException e) { - // throw new AccessException("Unable to access property '" + name + "' through getter", e); - // } - // } - // return null; - // } - // public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException - // { - // if (target==null) { - // return; - // } - // Class relevantClass = (target instanceof Class ? (Class) target : target.getClass()); - // if (!(name instanceof String)) - // return; - // Field field = ReflectionUtils.findField((String) name, relevantClass); - // if (field != null) { - // try { - // if (!field.isAccessible()) - // field.setAccessible(true); - // field.set(target, newValue); - // } catch (IllegalArgumentException e) { - // throw new AccessException("Unable to write to property '" + name + "'", e); - // } catch (IllegalAccessException e) { - // throw new AccessException("Unable to write to property '" + name + "'", e); - // } - // } - // Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass); - // if (m != null) { - // try { - // if (!m.isAccessible()) - // m.setAccessible(true); - // m.invoke(target, newValue); - // } catch (IllegalArgumentException e) { - // throw new AccessException("Unable to access property '" + name + "' through setter", e); - // } catch (IllegalAccessException e) { - // throw new AccessException("Unable to access property '" + name + "' through setter", e); - // } catch (InvocationTargetException e) { - // throw new AccessException("Unable to access property '" + name + "' through setter", e); - // } - // } - // } - // - // - // public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { - // if (target==null) { - // return false; - // } - // Class relevantClass = (target instanceof Class ? (Class) target : target.getClass()); - // if (!(name instanceof String)) - // return false; - // Field field = ReflectionUtils.findField((String) name, relevantClass); - // if (field != null) - // return true; - // Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass); - // if (m != null) - // return true; - // return false; - // } -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyWriterExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyWriterExecutor.java deleted file mode 100644 index aace6bad253..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyWriterExecutor.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.springframework.expression.spel.reflection; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import org.springframework.expression.AccessException; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.PropertyWriterExecutor; - -public class ReflectionPropertyWriterExecutor implements PropertyWriterExecutor { - - private Method methodToAccessProperty; - private Field fieldToAccessProperty; - private final String propertyName; - - public ReflectionPropertyWriterExecutor(String propertyName, Method method) { - this.propertyName = propertyName; - methodToAccessProperty = method; - } - - public ReflectionPropertyWriterExecutor(String propertyName, Field field) { - this.propertyName = propertyName; - fieldToAccessProperty = field; - } - - // public Object execute(EvaluationContext context, Object target) throws AccessException { - public void execute(EvaluationContext evaluationContext, Object target, Object newValue) throws AccessException { - if (methodToAccessProperty != null) { - try { - if (!methodToAccessProperty.isAccessible()) - methodToAccessProperty.setAccessible(true); - methodToAccessProperty.invoke(target, newValue); - return; - } catch (IllegalArgumentException e) { - throw new AccessException("Unable to access property '" + propertyName + "' through setter", e); - } catch (IllegalAccessException e) { - throw new AccessException("Unable to access property '" + propertyName + "' through setter", e); - } catch (InvocationTargetException e) { - throw new AccessException("Unable to access property '" + propertyName + "' through setter", e); - } - } - if (fieldToAccessProperty != null) { - try { - if (!fieldToAccessProperty.isAccessible()) { - fieldToAccessProperty.setAccessible(true); - } - fieldToAccessProperty.set(target, newValue); - return; - } catch (IllegalArgumentException e) { - throw new AccessException("Unable to access field: " + propertyName, e); - } catch (IllegalAccessException e) { - throw new AccessException("Unable to access field: " + propertyName, e); - } - } - throw new AccessException("No method or field accessor found for property '" + propertyName + "'"); - } -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionUtils.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionUtils.java deleted file mode 100644 index ff52788e99d..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/reflection/ReflectionUtils.java +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.reflection; - -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -import org.springframework.expression.EvaluationException; -import org.springframework.expression.TypeConverter; -import org.springframework.expression.spel.SpelException; -import org.springframework.expression.spel.SpelMessages; - -/** - * Utility methods used by the reflection resolver code to discover the correct methods/constructors and fields that - * should be used in expressions. - * - * @author Andy Clement - */ -@SuppressWarnings("unchecked") -public class ReflectionUtils { - - /** - * Locate a constructor on a type. There are three kinds of match that might occur: - *

    - *
  1. An exact match where the types of the arguments match the types of the constructor - *
  2. An in-exact match where the types we are looking for are subtypes of those defined on the constructor - *
  3. A match where we are able to convert the arguments into those expected by the constructor, according to the - * registered type converter. - *
- * - * @param typeConverter a converter that can be used to determine if the supplied arguments can be converted to - * expected arguments - * @param type the type being searched for a valid constructor - * @param argumentTypes the types of the arguments we want the constructor to have - * @return a DiscoveredConstructor object or null if non found - * @throws SpelException - */ - public static DiscoveredMethod findMethod(TypeConverter typeConverter, String name, Class[] argumentTypes, - Class type, boolean conversionAllowed) throws SpelException { - Method[] methods = type.getMethods(); - Method closeMatch = null; - Integer[] argsToConvert = null; - boolean multipleOptions = false; - Method matchRequiringConversion = null; - for (int i = 0; i < methods.length; i++) { - Method method = methods[i]; - if (method.isBridge()) { - continue; - } - if (method.getName().equals(name)) { - ArgumentsMatchInfo matchInfo = null; - if (method.isVarArgs() && argumentTypes.length >= (method.getParameterTypes().length - 1)) { - // *sigh* complicated - matchInfo = compareArgumentsVarargs(method.getParameterTypes(), argumentTypes, typeConverter, - conversionAllowed); - } else if (method.getParameterTypes().length == argumentTypes.length) { - // name and parameter number match, check the arguments - matchInfo = compareArguments(method.getParameterTypes(), argumentTypes, typeConverter, - conversionAllowed); - } - if (matchInfo != null) { - if (matchInfo.kind == ArgsMatchKind.EXACT) { - return new DiscoveredMethod(method, null); - } else if (matchInfo.kind == ArgsMatchKind.CLOSE) { - closeMatch = method; - } else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) { - if (matchRequiringConversion != null) { - multipleOptions = true; - } - argsToConvert = matchInfo.argsRequiringConversion; - matchRequiringConversion = method; - } - } - } - } - if (closeMatch != null) { - return new DiscoveredMethod(closeMatch, null); - } else if (matchRequiringConversion != null) { - if (multipleOptions) { - throw new SpelException(SpelMessages.MULTIPLE_POSSIBLE_METHODS, name); - } - return new DiscoveredMethod(matchRequiringConversion, argsToConvert); - } else { - return null; - } - } - - /** - * Locate a constructor on the type. There are three kinds of match that might occur: - *
    - *
  1. An exact match where the types of the arguments match the types of the constructor - *
  2. An in-exact match where the types we are looking for are subtypes of those defined on the constructor - *
  3. A match where we are able to convert the arguments into those expected by the constructor, according to the - * registered type converter. - *
- * - * @param typeConverter a converter that can be used to determine if the supplied arguments can be converted to - * expected arguments - * @param type the type being searched for a valid constructor - * @param argumentTypes the types of the arguments we want the constructor to have - * @return a DiscoveredConstructor object or null if non found - */ - public static DiscoveredConstructor findConstructor(TypeConverter typeConverter, Class type, - Class[] argumentTypes, boolean conversionAllowed) { - Constructor[] ctors = type.getConstructors(); - Constructor closeMatch = null; - Integer[] argsToConvert = null; - Constructor matchRequiringConversion = null; - for (int i = 0; i < ctors.length; i++) { - Constructor ctor = ctors[i]; - if (ctor.isVarArgs() && argumentTypes.length >= (ctor.getParameterTypes().length - 1)) { - // *sigh* complicated - // Basically.. we have to have all parameters match up until the varargs one, then the rest of what is - // being provided should be - // the same type whilst the final argument to the method must be an array of that (oh, how easy...not) - - // or the final parameter - // we are supplied does match exactly (it is an array already). - ArgumentsMatchInfo matchInfo = compareArgumentsVarargs(ctor.getParameterTypes(), argumentTypes, - typeConverter, conversionAllowed); - if (matchInfo != null) { - if (matchInfo.kind == ArgsMatchKind.EXACT) { - return new DiscoveredConstructor(ctor, null); - } else if (matchInfo.kind == ArgsMatchKind.CLOSE) { - closeMatch = ctor; - } else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) { - argsToConvert = matchInfo.argsRequiringConversion; - matchRequiringConversion = ctor; - } - } - - } else if (ctor.getParameterTypes().length == argumentTypes.length) { - // worth a closer look - ArgumentsMatchInfo matchInfo = compareArguments(ctor.getParameterTypes(), argumentTypes, typeConverter, - conversionAllowed); - if (matchInfo != null) { - if (matchInfo.kind == ArgsMatchKind.EXACT) { - return new DiscoveredConstructor(ctor, null); - } else if (matchInfo.kind == ArgsMatchKind.CLOSE) { - closeMatch = ctor; - } else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) { - argsToConvert = matchInfo.argsRequiringConversion; - matchRequiringConversion = ctor; - } - } - } - } - if (closeMatch != null) { - return new DiscoveredConstructor(closeMatch, null); - } else if (matchRequiringConversion != null) { - return new DiscoveredConstructor(matchRequiringConversion, argsToConvert); - } else { - return null; - } - } - - /** - * Compare argument arrays and return information about whether they match. A supplied type converter and - * conversionAllowed flag allow for matches to take into account that a type may be transformed into a different - * type by the converter. - * - * @param expectedArgTypes the array of types the method/constructor is expecting - * @param suppliedArgTypes the array of types that are being supplied at the point of invocation - * @param typeConverter a registered type converter - * @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can - * match an expected type - * @return a MatchInfo object indicating what kind of match it was or null if it was not a match - */ - private static ArgumentsMatchInfo compareArguments(Class[] expectedArgTypes, Class[] suppliedArgTypes, - TypeConverter typeConverter, boolean conversionAllowed) { - ArgsMatchKind match = ArgsMatchKind.EXACT; - List argsRequiringConversion = null; - for (int i = 0; i < expectedArgTypes.length && match != null; i++) { - Class suppliedArg = suppliedArgTypes[i]; - Class expectedArg = expectedArgTypes[i]; - if (expectedArg != suppliedArg) { - if (expectedArg.isAssignableFrom(suppliedArg) || areBoxingCompatible(expectedArg, suppliedArg) - /* || isWidenableTo(expectedArg, suppliedArg) */) { - if (match != ArgsMatchKind.REQUIRES_CONVERSION) { - match = ArgsMatchKind.CLOSE; - } - } else if (typeConverter.canConvert(suppliedArg, expectedArg)) { - if (argsRequiringConversion == null) { - argsRequiringConversion = new ArrayList(); - } - argsRequiringConversion.add(i); - match = ArgsMatchKind.REQUIRES_CONVERSION; - } else { - match = null; - } - } - } - if (match == null) { - return null; - } else { - if (match == ArgsMatchKind.REQUIRES_CONVERSION) { - return new ArgumentsMatchInfo(match, argsRequiringConversion.toArray(new Integer[] {})); - } else { - return new ArgumentsMatchInfo(match); - } - } - } - - /** - * Compare argument arrays and return information about whether they match. A supplied type converter and - * conversionAllowed flag allow for matches to take into account that a type may be transformed into a different - * type by the converter. This variant of compareArguments allows for a varargs match. - * - * @param expectedArgTypes the array of types the method/constructor is expecting - * @param suppliedArgTypes the array of types that are being supplied at the point of invocation - * @param typeConverter a registered type converter - * @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can - * match an expected type - * @return a MatchInfo object indicating what kind of match it was or null if it was not a match - */ - private static ArgumentsMatchInfo compareArgumentsVarargs(Class[] expectedArgTypes, Class[] suppliedArgTypes, - TypeConverter typeConverter, boolean conversionAllowed) { - ArgsMatchKind match = ArgsMatchKind.EXACT; - List argsRequiringConversion = null; - - // Check up until the varargs argument: - - // Deal with the arguments up to 'expected number' - 1 - for (int i = 0; i < expectedArgTypes.length - 1 && match != null; i++) { - Class suppliedArg = suppliedArgTypes[i]; - Class expectedArg = expectedArgTypes[i]; - if (expectedArg != suppliedArg) { - if (expectedArg.isAssignableFrom(suppliedArg) || areBoxingCompatible(expectedArg, suppliedArg) - /* || isWidenableTo(expectedArg, suppliedArg) */) { - if (match != ArgsMatchKind.REQUIRES_CONVERSION) { - match = ArgsMatchKind.CLOSE; - } - } else if (typeConverter.canConvert(suppliedArg, expectedArg)) { - if (argsRequiringConversion == null) { - argsRequiringConversion = new ArrayList(); - } - argsRequiringConversion.add(i); - match = ArgsMatchKind.REQUIRES_CONVERSION; - } else { - match = null; - } - } - } - // Already does not match - if (match == null) { - return null; - } - - // Special case: there is one parameter left and it is an array and it matches the varargs expected argument - - // that is a match, the caller has already built the array - if (suppliedArgTypes.length == expectedArgTypes.length - && expectedArgTypes[expectedArgTypes.length - 1] == suppliedArgTypes[suppliedArgTypes.length - 1]) { - - } else { - - // Now... we have the final argument in the method we are checking as a match and we have 0 or more other - // arguments left to pass to it. - Class varargsParameterType = expectedArgTypes[expectedArgTypes.length - 1].getComponentType(); - - // All remaining parameters must be of this type or convertable to this type - for (int i = expectedArgTypes.length - 1; i < suppliedArgTypes.length; i++) { - Class suppliedArg = suppliedArgTypes[i]; - if (varargsParameterType != suppliedArg) { - if (varargsParameterType.isAssignableFrom(suppliedArg) - || areBoxingCompatible(varargsParameterType, suppliedArg) - /* || isWidenableTo(expectedArg, suppliedArg) */) { - if (match != ArgsMatchKind.REQUIRES_CONVERSION) { - match = ArgsMatchKind.CLOSE; - } - } else if (typeConverter.canConvert(suppliedArg, varargsParameterType)) { - if (argsRequiringConversion == null) { - argsRequiringConversion = new ArrayList(); - } - argsRequiringConversion.add(i); - match = ArgsMatchKind.REQUIRES_CONVERSION; - } else { - match = null; - } - } - } - } - - if (match == null) { - return null; - } else { - if (match == ArgsMatchKind.REQUIRES_CONVERSION) { - return new ArgumentsMatchInfo(match, argsRequiringConversion.toArray(new Integer[] {})); - } else { - return new ArgumentsMatchInfo(match); - } - } - } - - // TODO optimize implementation of areBoxingCompatible - private static boolean areBoxingCompatible(Class class1, Class class2) { - if (class1 == Integer.class && class2 == Integer.TYPE) - return true; - if (class1 == Float.class && class2 == Float.TYPE) - return true; - if (class1 == Double.class && class2 == Double.TYPE) - return true; - if (class1 == Short.class && class2 == Short.TYPE) - return true; - if (class1 == Long.class && class2 == Long.TYPE) - return true; - if (class1 == Boolean.class && class2 == Boolean.TYPE) - return true; - if (class1 == Character.class && class2 == Character.TYPE) - return true; - if (class1 == Byte.class && class2 == Byte.TYPE) - return true; - if (class2 == Integer.class && class1 == Integer.TYPE) - return true; - if (class2 == Float.class && class1 == Float.TYPE) - return true; - if (class2 == Double.class && class1 == Double.TYPE) - return true; - if (class2 == Short.class && class1 == Short.TYPE) - return true; - if (class2 == Long.class && class1 == Long.TYPE) - return true; - if (class2 == Boolean.class && class1 == Boolean.TYPE) - return true; - if (class2 == Character.class && class1 == Character.TYPE) - return true; - if (class2 == Byte.class && class1 == Byte.TYPE) - return true; - return false; - } - - /** - * Find a field of a certain name on a specified class - */ - public final static Field findField(String name, Class clazz, boolean mustBeStatic) { - Field[] fields = clazz.getFields(); // TODO use getDeclaredFields() and search up hierarchy? - for (int i = 0; i < fields.length; i++) { - Field field = fields[i]; - if (field.getName().equals(name) && (mustBeStatic ? Modifier.isStatic(field.getModifiers()) : true)) { - return field; - } - } - return null; - } - - /** - * Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix - * 'get' and the rest of the name is the same as the property name (with the first character uppercased). - */ - public static Method findGetterForProperty(String propertyName, Class clazz, boolean mustBeStatic) { - Method[] ms = clazz.getMethods();// TODO use getDeclaredMethods() and search up hierarchy? - StringBuilder sb = new StringBuilder(); - sb.append("get").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1)); - String expectedGetterName = sb.toString(); - for (int i = 0; i < ms.length; i++) { - Method method = ms[i]; - if (method.getParameterTypes().length == 0 - && (mustBeStatic ? Modifier.isStatic(method.getModifiers()) : true) - && method.getName().equals(expectedGetterName)) { - return method; - } - } - return null; - } - - /** - * Find a setter method for the specified property - */ - public static Method findSetterForProperty(String propertyName, Class clazz, boolean mustBeStatic) { - Method[] ms = clazz.getMethods(); // TODO use getDeclaredMethods() and search up hierarchy? - StringBuilder sb = new StringBuilder(); - sb.append("set").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1)); - String setterName = sb.toString(); - for (int i = 0; i < ms.length; i++) { - Method method = ms[i]; - if (method.getParameterTypes().length == 1 - && (mustBeStatic ? Modifier.isStatic(method.getModifiers()) : true) - && method.getName().equals(setterName)) { - return method; - } - } - return null; - } - - /** - * An instance of MatchInfo describes what kind of match was achieved between two sets of arguments - the set that a - * method/constructor is expecting and the set that are being supplied at the point of invocation. If the kind - * indicates that conversion is required for some of the arguments then the arguments that require conversion are - * listed in the argsRequiringConversion array. - * - */ - private static class ArgumentsMatchInfo { - ArgsMatchKind kind; - Integer[] argsRequiringConversion; - - ArgumentsMatchInfo(ArgsMatchKind kind, Integer[] integers) { - this.kind = kind; - argsRequiringConversion = integers; - } - - ArgumentsMatchInfo(ArgsMatchKind kind) { - this.kind = kind; - } - } - - private static enum ArgsMatchKind { - EXACT, CLOSE, REQUIRES_CONVERSION; - } - - /** - * When a match is found searching for a particular constructor, this object captures the constructor object and - * details of which arguments require conversion for the call to be allowed. - */ - public static class DiscoveredConstructor { - public Constructor theConstructor; - public Integer[] argumentsRequiringConversion; - - public DiscoveredConstructor(Constructor theConstructor, Integer[] argsToConvert) { - this.theConstructor = theConstructor; - argumentsRequiringConversion = argsToConvert; - } - } - - /** - * When a match is found searching for a particular method, this object captures the method object and details of - * which arguments require conversion for the call to be allowed. - */ - public static class DiscoveredMethod { - public Method theMethod; - public Integer[] argumentsRequiringConversion; - - public DiscoveredMethod(Method theMethod, Integer[] argsToConvert) { - this.theMethod = theMethod; - argumentsRequiringConversion = argsToConvert; - } - } - - static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter, - Integer[] argsRequiringConversion, Object... arguments) throws EvaluationException { - Class varargsType = null; - if (isVarargs) { - varargsType = parameterTypes[parameterTypes.length - 1].getComponentType(); - } - for (int i = 0; i < argsRequiringConversion.length; i++) { - int argPosition = argsRequiringConversion[i]; - Class targetType = null; - if (isVarargs && argPosition >= (parameterTypes.length - 1)) { - targetType = varargsType; - } else { - targetType = parameterTypes[argPosition]; - } - // try { - arguments[argPosition] = converter.convertValue(arguments[argPosition], targetType); - // } catch (EvaluationException e) { - // throw new SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "Converter failed to convert '" - // + arguments[argPosition] + " to type '" + targetType + "'"); - // } - } - } - - public static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter, - Object... arguments) throws EvaluationException { - Class varargsType = null; - if (isVarargs) { - varargsType = parameterTypes[parameterTypes.length - 1].getComponentType(); - } - for (int i = 0; i < arguments.length; i++) { - Class targetType = null; - if (isVarargs && i >= (parameterTypes.length - 1)) { - targetType = varargsType; - } else { - targetType = parameterTypes[i]; - } - if (converter == null) { - throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, - "No converter available to convert '" + arguments[i] + " to type '" + targetType + "'"); - } - try { - if (arguments[i] != null && arguments[i].getClass() != targetType) { - arguments[i] = converter.convertValue(arguments[i], targetType); - } - } catch (EvaluationException e) { - // allows for another type converter throwing a different kind of EvaluationException - if (!(e instanceof SpelException)) { - throw new SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, - "Converter failed to convert '" + arguments[i].getClass().getName() + "' to type '" - + targetType + "'"); - } - throw e; - } - } - } - - /** - * Package up the arguments so that they correctly match what is expected in parameterTypes. For example, if - * parameterTypes is (int, String[]) because the second parameter was declared String... then if arguments is - * [1,"a","b"] then it must be repackaged as [1,new String[]{"a","b"}] in order to match the expected - * parameterTypes. - * - * @param parameterTypes the types of the parameters for the invocation - * @param arguments the arguments to be setup ready for the invocation - * @return a repackaged array of arguments where any varargs setup has been done - */ - public static Object[] setupArgumentsForVarargsInvocation(Class[] parameterTypes, Object... arguments) { - // Check if array already built for final argument - int nParams = parameterTypes.length; - int nArgs = arguments.length; - - // Check if repackaging is needed: - if (nParams != arguments.length - || parameterTypes[nParams - 1] != (arguments[nArgs - 1] == null ? null : arguments[nArgs - 1] - .getClass())) { - int arraySize = 0; // zero size array if nothing to pass as the varargs parameter - if (arguments != null && nArgs >= nParams) { - arraySize = nArgs - (nParams - 1); - } - Object[] repackagedArguments = (Object[]) Array.newInstance(parameterTypes[nParams - 1].getComponentType(), - arraySize); - - // Copy all but the varargs arguments - for (int i = 0; i < arraySize; i++) { - repackagedArguments[i] = arguments[nParams + i - 1]; - } - // Create an array for the varargs arguments - Object[] newArgs = new Object[nParams]; - for (int i = 0; i < newArgs.length - 1; i++) { - newArgs[i] = arguments[i]; - } - newArgs[newArgs.length - 1] = repackagedArguments; - return newArgs; - } - return arguments; - } - - public static Object[] prepareArguments(TypeConverter converter, Method m, Object[] arguments) - throws EvaluationException { - if (arguments != null) { - ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, arguments); - } - if (m.isVarArgs()) { - arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), arguments); - } - return arguments; - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardEvaluationContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardEvaluationContext.java deleted file mode 100644 index 8a7e7a7ebd6..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardEvaluationContext.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.standard; - -import java.io.File; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; - -import org.springframework.expression.ConstructorResolver; -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.MethodResolver; -import org.springframework.expression.PropertyAccessor; -import org.springframework.expression.TypeLocator; -import org.springframework.expression.TypeUtils; -import org.springframework.expression.spel.reflection.ReflectionConstructorResolver; -import org.springframework.expression.spel.reflection.ReflectionMethodResolver; -import org.springframework.expression.spel.reflection.ReflectionPropertyResolver; - -/** - * Provides a default EvaluationContext implementation. - *

- * To resolved properties/methods/fields this context uses a reflection mechanism. - * - * @author Andy Clement - * - */ -public class StandardEvaluationContext implements EvaluationContext { - - private Object rootObject; - private StandardTypeUtilities typeUtils; - private final Map variables = new HashMap(); - private final List methodResolvers = new ArrayList(); - private final List constructorResolvers = new ArrayList(); - private final List propertyResolvers = new ArrayList(); - private final Map> simpleReferencesMap = new HashMap>(); - - public StandardEvaluationContext() { - typeUtils = new StandardTypeUtilities(); - addMethodResolver(new ReflectionMethodResolver()); - addConstructorResolver(new ReflectionConstructorResolver()); - addPropertyAccessor(new ReflectionPropertyResolver()); - } - - public void reset() { - typeUtils = new StandardTypeUtilities(); - methodResolvers.clear(); - addMethodResolver(new ReflectionMethodResolver()); - constructorResolvers.clear(); - addConstructorResolver(new ReflectionConstructorResolver()); - propertyResolvers.clear(); - addPropertyAccessor(new ReflectionPropertyResolver()); - simpleReferencesMap.clear(); - variables.clear(); - rootObject = null; - } - - public StandardEvaluationContext(Object rootContextObject) { - this(); - rootObject = rootContextObject; - } - - public void setClassLoader(ClassLoader loader) { - TypeLocator tLocator = typeUtils.getTypeLocator(); - if (tLocator instanceof StandardTypeLocator) { - ((StandardTypeLocator) tLocator).setClassLoader(loader); - } - } - - public void registerImport(String importPrefix) { - TypeLocator tLocator = typeUtils.getTypeLocator(); - if (tLocator instanceof StandardTypeLocator) { - ((StandardTypeLocator) tLocator).registerImport(importPrefix); - } - } - - public void setClasspath(String classpath) { - StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator); - List urls = new ArrayList(); - while (st.hasMoreTokens()) { - String element = st.nextToken(); - try { - urls.add(new File(element).toURI().toURL()); - } catch (MalformedURLException e) { - throw new RuntimeException("Invalid element in classpath " + element); - } - } - ClassLoader cl = new URLClassLoader(urls.toArray(new URL[] {}), Thread.currentThread().getContextClassLoader()); - TypeLocator tLocator = typeUtils.getTypeLocator(); - if (tLocator instanceof StandardTypeLocator) { - ((StandardTypeLocator) tLocator).setClassLoader(cl); - } - } - - public Object lookupVariable(String name) { - return variables.get(name); - } - - public TypeUtils getTypeUtils() { - return typeUtils; - } - - public Object getRootContextObject() { - return rootObject; - } - - public Object lookupReference(Object contextName, Object objectName) { - String contextToLookup = (contextName == null ? "root" : (String) contextName); - // if (contextName==null) return simpleReferencesMap; - Map contextMap = simpleReferencesMap.get(contextToLookup); - if (contextMap == null) - return null; - if (objectName == null) - return contextMap; - return contextMap.get(objectName); - } - - public List getPropertyAccessors() { - return propertyResolvers; - } - - public void addPropertyAccessor(PropertyAccessor accessor) { - propertyResolvers.add(accessor); - } - - public void removePropertyAccessor(PropertyAccessor accessor) { - propertyResolvers.remove(accessor); - } - - public void insertPropertyAccessor(int position, PropertyAccessor accessor) { - propertyResolvers.add(position, accessor); - } - - public List getMethodResolvers() { - return methodResolvers; - } - - public List getConstructorResolvers() { - return constructorResolvers; - } - - public void setVariable(String name, Object value) { - variables.put(name, value); - } - - public void registerFunction(String name, Method m) { - variables.put(name, m); - } - - public void setRootObject(Object o) { - rootObject = o; - } - - // TODO have a variant that adds at position (same for ctor/propOrField) - public void addMethodResolver(MethodResolver resolver) { - methodResolvers.add(resolver); - } - - public void removeMethodResolver(MethodResolver resolver) { - methodResolvers.remove(resolver); - } - - public void insertMethodResolver(int pos, MethodResolver resolver) { - methodResolvers.add(pos, resolver); - } - - public void addConstructorResolver(ConstructorResolver resolver) { - constructorResolvers.add(resolver); - } - - public void addReference(String contextName, String objectName, Object value) { - Map contextMap = simpleReferencesMap.get(contextName); - if (contextMap == null) { - contextMap = new HashMap(); - simpleReferencesMap.put(contextName, contextMap); - } - contextMap.put(objectName, value); - } - - public void addTypeConverter(StandardIndividualTypeConverter newConverter) { - ((StandardTypeConverter) typeUtils.getTypeConverter()).registerConverter(newConverter); - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardIndividualTypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardIndividualTypeConverter.java deleted file mode 100644 index 640411624e4..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardIndividualTypeConverter.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2004-2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.standard; - -import org.springframework.expression.EvaluationException; - -/** - * Implementations of this interface are able to convert from some set of types to another type. For - * example they might be able to convert some set of number types (Integer.class, Double.class) to - * a string (String.class). Once created they are registered with the {@link StandardEvaluationContext} or - * {@link StandardTypeConverter}. - * - * @author Andy Clement - */ -public interface StandardIndividualTypeConverter { - - /** - * @return return the set of classes which this converter can convert from. - */ - Class[] getFrom(); - - /** - * @return the class which this converter can convert to. - */ - Class getTo(); - - /** - * Return a value converted to the type that {@link #getTo()} specified. - * - * @param value the object to convert - * @return the converted value - * @throws EvaluationException if there is a problem during conversion - */ - Object convert(Object value) throws EvaluationException; -} \ No newline at end of file diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeConverter.java deleted file mode 100644 index 60b27cbb860..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeConverter.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.standard; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.expression.EvaluationException; -import org.springframework.expression.TypeConverter; -import org.springframework.expression.spel.SpelException; -import org.springframework.expression.spel.SpelMessages; - -public class StandardTypeConverter implements TypeConverter { - - public Map, Map, StandardIndividualTypeConverter>> converters = new HashMap, Map, StandardIndividualTypeConverter>>(); - - StandardTypeConverter() { - registerConverter(new ToBooleanConverter()); - registerConverter(new ToCharacterConverter()); - registerConverter(new ToShortConverter()); - registerConverter(new ToLongConverter()); - registerConverter(new ToDoubleConverter()); - registerConverter(new ToFloatConverter()); - registerConverter(new ToStringConverter()); - registerConverter(new ToIntegerConverter()); - registerConverter(new ToByteConverter()); - } - - public boolean canConvert(Class sourceType, Class targetType) { - Map, StandardIndividualTypeConverter> possibleConvertersToTheTargetType = converters.get(targetType); - if (possibleConvertersToTheTargetType == null && targetType.isPrimitive()) { - if (targetType == Integer.TYPE) { - if (sourceType == Integer.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Integer.class); - } else if (targetType == Boolean.TYPE) { - if (sourceType == Boolean.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Boolean.class); - } else if (targetType == Short.TYPE) { - if (sourceType == Short.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Short.class); - } else if (targetType == Long.TYPE) { - if (sourceType == Long.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Long.class); - } else if (targetType == Character.TYPE) { - if (sourceType == Character.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Character.class); - } else if (targetType == Double.TYPE) { - if (sourceType == Double.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Double.class); - } else if (targetType == Float.TYPE) { - if (sourceType == Float.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Float.class); - } else if (targetType == Byte.TYPE) { - if (sourceType == Byte.class) { - return true; - } - possibleConvertersToTheTargetType = converters.get(Byte.class); - } - } - if (possibleConvertersToTheTargetType != null) { - StandardIndividualTypeConverter aConverter = possibleConvertersToTheTargetType.get(sourceType); - if (aConverter != null) { - return true; - } - } - return false; - } - - // TODO In case of a loss in information with coercion to a narrower type, should we throw an exception? - public Object convertValue(Object value, Class targetType) throws SpelException { - if (value == null || value.getClass() == targetType) - return value; - Class sourceType = value.getClass(); - Map, StandardIndividualTypeConverter> possibleConvertersToTheTargetType = converters.get(targetType); - if (possibleConvertersToTheTargetType == null && targetType.isPrimitive()) { - if (targetType == Integer.TYPE) { - if (sourceType == Integer.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Integer.class); - } else if (targetType == Boolean.TYPE) { - if (sourceType == Boolean.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Boolean.class); - } else if (targetType == Short.TYPE) { - if (sourceType == Short.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Short.class); - } else if (targetType == Long.TYPE) { - if (sourceType == Long.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Long.class); - } else if (targetType == Character.TYPE) { - if (sourceType == Character.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Character.class); - } else if (targetType == Double.TYPE) { - if (sourceType == Double.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Double.class); - } else if (targetType == Float.TYPE) { - if (sourceType == Float.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Float.class); - } else if (targetType == Byte.TYPE) { - if (sourceType == Byte.class) { - return value; - } - possibleConvertersToTheTargetType = converters.get(Byte.class); - } - } - Object result = null; - if (possibleConvertersToTheTargetType != null) { - StandardIndividualTypeConverter aConverter = possibleConvertersToTheTargetType.get(value.getClass()); - if (aConverter != null) { - try { - result = aConverter.convert(value); - } catch (EvaluationException ee) { - if (ee instanceof SpelException) { - throw (SpelException) ee; - } else { - throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, ee.getMessage()); - } - } - } - } - if (result != null) - return result; - throw new SpelException(SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), targetType); - } - - public void registerConverter(StandardIndividualTypeConverter aConverter) { - Class toType = aConverter.getTo(); - Map, StandardIndividualTypeConverter> convertersResultingInSameType = converters.get(toType); - if (convertersResultingInSameType == null) { - convertersResultingInSameType = new HashMap, StandardIndividualTypeConverter>(); - } - Class[] fromTypes = aConverter.getFrom(); - for (int i = 0; i < fromTypes.length; i++) { - convertersResultingInSameType.put(fromTypes[i], aConverter); - } - converters.put(aConverter.getTo(), convertersResultingInSameType); - } - - private static class ToBooleanConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - if (value instanceof Integer) { - return ((Integer) value).intValue() != 0; - } else { - return ((Long) value).longValue() != 0; - } - } - - public Class[] getFrom() { - return new Class[] { Integer.class, Long.class }; - } - - public Class getTo() { - return Boolean.class; - } - } - - private static class ToDoubleConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - if (value instanceof Double) { - return ((Double) value).doubleValue(); - } else if (value instanceof String) { - try { - Double.parseDouble((String) value); - } catch (NumberFormatException nfe) { - // returning null will mean the caller throws a type conversion related exception - } - } else if (value instanceof Integer) { - return new Double(((Integer) value).intValue()); - } - return null; - } - - public Class[] getFrom() { - return new Class[] { Double.class, String.class, Integer.class }; - } - - public Class getTo() { - return Double.class; - } - } - - private static class ToFloatConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - if (value instanceof Integer) { - return ((Integer)value).floatValue(); - } else { - return ((Double) value).floatValue(); - } - } - - public Class[] getFrom() { - return new Class[] { Double.class, Integer.class }; - } - - public Class getTo() { - return Float.class; - } - } - - private static class ToByteConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - return ((Integer) value).byteValue(); - } - - public Class[] getFrom() { - return new Class[] { Integer.class }; - } - - public Class getTo() { - return Byte.class; - } - } - - private static class ToLongConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - if (value instanceof Integer) - return ((Integer) value).longValue(); - else if (value instanceof Short) - return ((Short) value).longValue(); - else if (value instanceof Byte) - return ((Byte) value).longValue(); - return null; - } - - public Class[] getFrom() { - return new Class[] { Integer.class, Short.class, Byte.class }; - } - - public Class getTo() { - return Long.class; - } - } - - private static class ToCharacterConverter implements StandardIndividualTypeConverter { - public Character convert(Object value) throws SpelException { - if (value instanceof Integer) - return ((char) ((Integer) value).intValue()); - if (value instanceof String) { - String s = (String) value; - if (s.length() == 1) - return s.charAt(0); - } - return null; - } - - public Class[] getFrom() { - return new Class[] { Integer.class, String.class }; - } - - public Class getTo() { - return Character.class; - } - } - - private static class ToShortConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - if (value instanceof Integer) - return ((short) ((Integer) value).shortValue()); - return null; - } - - public Class[] getFrom() { - return new Class[] { Integer.class }; - } - - public Class getTo() { - return Short.class; - } - } - - private static class ToStringConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - return value.toString(); - } - - public Class[] getFrom() { - return new Class[] { Integer.class, Double.class }; - } - - public Class getTo() { - return String.class; - } - } - - private static class ToIntegerConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws SpelException { - if (value instanceof String) { - try { - return Integer.parseInt((String)value); - } catch (NumberFormatException nfe) { - throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "cannot parse string '" + value - + "' as an integer"); - } - } else { // Long - try { - return Integer.parseInt(((Long) value).toString()); - } catch (NumberFormatException nfe) { - throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "long value '" + value - + "' cannot be represented as an int"); - } - } - } - - public Class[] getFrom() { - return new Class[] { Long.class, String.class }; - } - - public Class getTo() { - return Integer.class; - } - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeUtilities.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeUtilities.java deleted file mode 100644 index 34012d3cffd..00000000000 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeUtilities.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel.standard; - -import org.springframework.expression.OperatorOverloader; -import org.springframework.expression.TypeComparator; -import org.springframework.expression.TypeConverter; -import org.springframework.expression.TypeLocator; -import org.springframework.expression.TypeUtils; - -/** - * The StandardTypeUtilities implementation pulls together the standard implementations of the TypeComparator, - * TypeLocator and TypeConverter interfaces. Each of these can be replaced so if only wishing to replace one of those - * type facilities. - * - * @author Andy Clement - * - */ -public class StandardTypeUtilities implements TypeUtils { - - private TypeComparator typeComparator; - private TypeLocator typeLocator; - private TypeConverter typeConverter; - private OperatorOverloader operatorOverloader; - - public StandardTypeUtilities() { - typeComparator = new StandardComparator(); - typeLocator = new StandardTypeLocator(); - typeConverter = new StandardTypeConverter(); - operatorOverloader = null; // this means operations between basic types are supported (eg. numbers) - } - - public TypeLocator getTypeLocator() { - return typeLocator; - } - - /** - * Set the type locator for the StandardTypeUtilities object, allows a user to replace parts of the standard - * TypeUtilities implementation if they wish. - * - * @param typeLocator the TypeLocator to use from now on - */ - public void setTypeLocator(TypeLocator typeLocator) { - this.typeLocator = typeLocator; - } - - public TypeConverter getTypeConverter() { - return typeConverter; - } - - /** - * Set the type converter for the StandardTypeUtilities object, allows a user to replace parts of the standard - * TypeUtilities implementation if they wish. - * - * @param typeConverter the TypeConverter to use from now on - */ - public void setTypeConverter(TypeConverter typeConverter) { - this.typeConverter = typeConverter; - } - - public TypeComparator getTypeComparator() { - return typeComparator; - } - - /** - * Set the type comparator for the StandardTypeUtilities object, allows a user to replace parts of the standard - * TypeUtilities implementation if they wish. - * - * @param typeComparator the TypeComparator to use from now on - */ - public void setTypeComparator(TypeComparator typeComparator) { - this.typeComparator = typeComparator; - } - - public OperatorOverloader getOperatorOverloader() { - return operatorOverloader; - } - - /** - * Set the operator overloader for the StandardTypeUtilities object, allows a user to overload the mathematical - * operators to support them between non-standard types. - * - * @param operatorOverloader the OperatorOverloader to use from now on - */ - public void setOperatorOverloader(OperatorOverloader operatorOverloader) { - this.operatorOverloader = operatorOverloader; - } - -} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java new file mode 100644 index 00000000000..4ddc1d42ebe --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -0,0 +1,315 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.support; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.expression.EvaluationException; +import org.springframework.expression.TypeConverter; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.SpelMessages; +import org.springframework.util.ClassUtils; + +/** + * Utility methods used by the reflection resolver code to discover the appropriae + * methods/constructors and fields that should be used in expressions. + * + * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 + */ +public class ReflectionHelper { + + /** + * Compare argument arrays and return information about whether they match. A supplied type converter and + * conversionAllowed flag allow for matches to take into account that a type may be transformed into a different + * type by the converter. + * @param expectedArgTypes the array of types the method/constructor is expecting + * @param suppliedArgTypes the array of types that are being supplied at the point of invocation + * @param typeConverter a registered type converter + * @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can + * match an expected type + * @return a MatchInfo object indicating what kind of match it was or null if it was not a match + */ + static ArgumentsMatchInfo compareArguments( + Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) { + + ArgsMatchKind match = ArgsMatchKind.EXACT; + List argsRequiringConversion = null; + for (int i = 0; i < expectedArgTypes.length && match != null; i++) { + Class suppliedArg = suppliedArgTypes[i]; + Class expectedArg = expectedArgTypes[i]; + if (expectedArg != suppliedArg) { + if (ClassUtils.isAssignable(expectedArg, suppliedArg) + /* || isWidenableTo(expectedArg, suppliedArg) */) { + if (match != ArgsMatchKind.REQUIRES_CONVERSION) { + match = ArgsMatchKind.CLOSE; + } + } else if (typeConverter.canConvert(suppliedArg, expectedArg)) { + if (argsRequiringConversion == null) { + argsRequiringConversion = new ArrayList(); + } + argsRequiringConversion.add(i); + match = ArgsMatchKind.REQUIRES_CONVERSION; + } else { + match = null; + } + } + } + if (match == null) { + return null; + } + else { + if (match == ArgsMatchKind.REQUIRES_CONVERSION) { + int[] argsArray = new int[argsRequiringConversion.size()]; + for (int i = 0; i < argsRequiringConversion.size(); i++) { + argsArray[i] = argsRequiringConversion.get(i); + } + return new ArgumentsMatchInfo(match, argsArray); + } + else { + return new ArgumentsMatchInfo(match); + } + } + } + + /** + * Compare argument arrays and return information about whether they match. A supplied type converter and + * conversionAllowed flag allow for matches to take into account that a type may be transformed into a different + * type by the converter. This variant of compareArguments allows for a varargs match. + * @param expectedArgTypes the array of types the method/constructor is expecting + * @param suppliedArgTypes the array of types that are being supplied at the point of invocation + * @param typeConverter a registered type converter + * @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can + * match an expected type + * @return a MatchInfo object indicating what kind of match it was or null if it was not a match + */ + static ArgumentsMatchInfo compareArgumentsVarargs( + Class[] expectedArgTypes, Class[] suppliedArgTypes, TypeConverter typeConverter) { + + ArgsMatchKind match = ArgsMatchKind.EXACT; + List argsRequiringConversion = null; + + // Check up until the varargs argument: + + // Deal with the arguments up to 'expected number' - 1 + for (int i = 0; i < expectedArgTypes.length - 1 && match != null; i++) { + Class suppliedArg = suppliedArgTypes[i]; + Class expectedArg = expectedArgTypes[i]; + if (expectedArg != suppliedArg) { + if (expectedArg.isAssignableFrom(suppliedArg) || ClassUtils.isAssignableValue(expectedArg, suppliedArg)) { + if (match != ArgsMatchKind.REQUIRES_CONVERSION) { + match = ArgsMatchKind.CLOSE; + } + } + else if (typeConverter.canConvert(suppliedArg, expectedArg)) { + if (argsRequiringConversion == null) { + argsRequiringConversion = new ArrayList(); + } + argsRequiringConversion.add(i); + match = ArgsMatchKind.REQUIRES_CONVERSION; + } else { + match = null; + } + } + } + // Already does not match + if (match == null) { + return null; + } + + // Special case: there is one parameter left and it is an array and it matches the varargs expected argument - + // that is a match, the caller has already built the array + if (suppliedArgTypes.length == expectedArgTypes.length + && expectedArgTypes[expectedArgTypes.length - 1] == suppliedArgTypes[suppliedArgTypes.length - 1]) { + + } else { + + // Now... we have the final argument in the method we are checking as a match and we have 0 or more other + // arguments left to pass to it. + Class varargsParameterType = expectedArgTypes[expectedArgTypes.length - 1].getComponentType(); + + // All remaining parameters must be of this type or convertable to this type + for (int i = expectedArgTypes.length - 1; i < suppliedArgTypes.length; i++) { + Class suppliedArg = suppliedArgTypes[i]; + if (varargsParameterType != suppliedArg) { + if (ClassUtils.isAssignable(varargsParameterType, suppliedArg)) { + if (match != ArgsMatchKind.REQUIRES_CONVERSION) { + match = ArgsMatchKind.CLOSE; + } + } + else if (typeConverter.canConvert(suppliedArg, varargsParameterType)) { + if (argsRequiringConversion == null) { + argsRequiringConversion = new ArrayList(); + } + argsRequiringConversion.add(i); + match = ArgsMatchKind.REQUIRES_CONVERSION; + } + else { + match = null; + } + } + } + } + + if (match == null) { + return null; + } + else { + if (match == ArgsMatchKind.REQUIRES_CONVERSION) { + int[] argsArray = new int[argsRequiringConversion.size()]; + for (int i = 0; i < argsRequiringConversion.size(); i++) { + argsArray[i] = argsRequiringConversion.get(i); + } + return new ArgumentsMatchInfo(match, argsArray); + } + else { + return new ArgumentsMatchInfo(match); + } + } + } + + static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter, + int[] argsRequiringConversion, Object... arguments) throws EvaluationException { + Class varargsType = null; + if (isVarargs) { + varargsType = parameterTypes[parameterTypes.length - 1].getComponentType(); + } + for (Integer argPosition : argsRequiringConversion) { + Class targetType = null; + if (isVarargs && argPosition >= (parameterTypes.length - 1)) { + targetType = varargsType; + } + else { + targetType = parameterTypes[argPosition]; + } + // try { + arguments[argPosition] = converter.convertValue(arguments[argPosition], targetType); + // } catch (EvaluationException e) { + // throw new SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "Converter failed to convert '" + // + arguments[argPosition] + " to type '" + targetType + "'"); + // } + } + } + + public static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter, + Object... arguments) throws EvaluationException { + + Class varargsType = null; + if (isVarargs) { + varargsType = parameterTypes[parameterTypes.length - 1].getComponentType(); + } + for (int i = 0; i < arguments.length; i++) { + Class targetType = null; + if (isVarargs && i >= (parameterTypes.length - 1)) { + targetType = varargsType; + } + else { + targetType = parameterTypes[i]; + } + if (converter == null) { + throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, + "No converter available to convert '" + arguments[i] + " to type '" + targetType + "'"); + } + try { + if (arguments[i] != null && arguments[i].getClass() != targetType) { + arguments[i] = converter.convertValue(arguments[i], targetType); + } + } + catch (EvaluationException ex) { + // allows for another type converter throwing a different kind of EvaluationException + if (ex instanceof SpelException) { + throw ex; + } + else { + throw new SpelException(ex, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, + "Converter failed to convert '" + arguments[i].getClass().getName() + "' to type '" + targetType + "'"); + } + } + } + } + + /** + * Package up the arguments so that they correctly match what is expected in parameterTypes. For example, if + * parameterTypes is (int, String[]) because the second parameter was declared String... then if arguments is + * [1,"a","b"] then it must be repackaged as [1,new String[]{"a","b"}] in order to match the expected + * parameterTypes. + * @param paramTypes the types of the parameters for the invocation + * @param args the arguments to be setup ready for the invocation + * @return a repackaged array of arguments where any varargs setup has been done + */ + public static Object[] setupArgumentsForVarargsInvocation(Class[] paramTypes, Object... args) { + // Check if array already built for final argument + int nParams = paramTypes.length; + int nArgs = args.length; + + // Check if repackaging is needed: + if (nParams != args.length || paramTypes[nParams - 1] != (args[nArgs - 1] == null ? null : args[nArgs - 1].getClass())) { + int arraySize = 0; // zero size array if nothing to pass as the varargs parameter + if (nArgs >= nParams) { + arraySize = nArgs - (nParams - 1); + } + Object[] repackagedArguments = (Object[]) Array.newInstance(paramTypes[nParams - 1].getComponentType(), + arraySize); + + // Copy all but the varargs arguments + for (int i = 0; i < arraySize; i++) { + repackagedArguments[i] = args[nParams + i - 1]; + } + // Create an array for the varargs arguments + Object[] newArgs = new Object[nParams]; + for (int i = 0; i < newArgs.length - 1; i++) { + newArgs[i] = args[i]; + } + newArgs[newArgs.length - 1] = repackagedArguments; + return newArgs; + } + return args; + } + + + static enum ArgsMatchKind { + + EXACT, CLOSE, REQUIRES_CONVERSION + } + + + /** + * An instance of ArgumentsMatchInfo describes what kind of match was achieved between two sets of arguments - the set that a + * method/constructor is expecting and the set that are being supplied at the point of invocation. If the kind + * indicates that conversion is required for some of the arguments then the arguments that require conversion are + * listed in the argsRequiringConversion array. + */ + static class ArgumentsMatchInfo { + + public ArgsMatchKind kind; + + public int[] argsRequiringConversion; + + ArgumentsMatchInfo(ArgsMatchKind kind, int[] integers) { + this.kind = kind; + argsRequiringConversion = integers; + } + + ArgumentsMatchInfo(ArgsMatchKind kind) { + this.kind = kind; + } + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java new file mode 100644 index 00000000000..21bda82d59c --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java @@ -0,0 +1,64 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.support; + +import java.lang.reflect.Constructor; + +import org.springframework.expression.AccessException; +import org.springframework.expression.ConstructorExecutor; +import org.springframework.expression.EvaluationContext; + +/** + * A simple ConstructorExecutor implementation that runs a constructor using reflective invocation. + * + * @author Andy Clement + * @since 3.0 + */ +class ReflectiveConstructorExecutor implements ConstructorExecutor { + + private final Constructor c; + + // When the constructor was found, we will have determined if arguments need to be converted for it + // to be invoked. Conversion won't be cheap so let's only do it if necessary. + private final int[] argsRequiringConversion; + + + public ReflectiveConstructorExecutor(Constructor constructor, int[] argsRequiringConversion) { + c = constructor; + this.argsRequiringConversion = argsRequiringConversion; + } + + public Object execute(EvaluationContext context, Object... arguments) throws AccessException { + try { + if (argsRequiringConversion != null && arguments != null) { + ReflectionHelper.convertArguments(c.getParameterTypes(), c.isVarArgs(), + context.getTypeConverter(), argsRequiringConversion, arguments); + } + if (c.isVarArgs()) { + arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(c.getParameterTypes(), arguments); + } + if (!c.isAccessible()) { + c.setAccessible(true); + } + return c.newInstance(arguments); + } + catch (Exception ex) { + throw new AccessException("Problem invoking constructor: " + c, ex); + } + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorResolver.java new file mode 100644 index 00000000000..414708f48d4 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorResolver.java @@ -0,0 +1,98 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.support; + +import java.lang.reflect.Constructor; + +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: + *

    + *
  1. An exact match where the types of the arguments match the types of the constructor + *
  2. An in-exact match where the types we are looking for are subtypes of those defined on the constructor + *
  3. A match where we are able to convert the arguments into those expected by the constructor, according to the + * registered type converter. + *
+ */ + public ConstructorExecutor resolve(EvaluationContext context, String typename, Class[] argumentTypes) + throws AccessException { + try { + TypeConverter typeConverter = context.getTypeConverter(); + Class type = context.getTypeLocator().findType(typename); + Constructor[] ctors = type.getConstructors(); + Constructor closeMatch = null; + int[] argsToConvert = null; + Constructor matchRequiringConversion = null; + for (Constructor ctor : ctors) { + ReflectionHelper.ArgumentsMatchInfo matchInfo = null; + if (ctor.isVarArgs() && argumentTypes.length >= (ctor.getParameterTypes().length - 1)) { + // *sigh* complicated + // Basically.. we have to have all parameters match up until the varargs one, then the rest of what is + // being provided should be + // the same type whilst the final argument to the method must be an array of that (oh, how easy...not) - + // or the final parameter + // we are supplied does match exactly (it is an array already). + matchInfo = ReflectionHelper.compareArgumentsVarargs(ctor.getParameterTypes(), argumentTypes, typeConverter); + } + else if (ctor.getParameterTypes().length == argumentTypes.length) { + // worth a closer look + matchInfo = ReflectionHelper.compareArguments(ctor.getParameterTypes(), 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); + } + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java new file mode 100644 index 00000000000..c64e48a5cfe --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodExecutor.java @@ -0,0 +1,62 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.support; + +import java.lang.reflect.Method; + +import org.springframework.expression.AccessException; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.MethodExecutor; +import org.springframework.util.ReflectionUtils; + +/** + * @author Andy Clement + * @since 3.0 + */ +class ReflectiveMethodExecutor implements MethodExecutor { + + private final Method method; + + // 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; + this.argsRequiringConversion = argumentsRequiringConversion; + } + + + public Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException { + try { + if (this.argsRequiringConversion != null && arguments != null) { + ReflectionHelper.convertArguments(this.method.getParameterTypes(), this.method.isVarArgs(), + context.getTypeConverter(), this.argsRequiringConversion, arguments); + } + if (this.method.isVarArgs()) { + arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments); + } + ReflectionUtils.makeAccessible(this.method); + return this.method.invoke(target, arguments); + } + catch (Exception ex) { + throw new AccessException("Problem invoking method: " + this.method, ex); + } + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java new file mode 100644 index 00000000000..92c80cedfaa --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.support; + +import java.lang.reflect.Method; + +import org.springframework.expression.AccessException; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.EvaluationException; +import org.springframework.expression.MethodExecutor; +import org.springframework.expression.MethodResolver; +import org.springframework.expression.TypeConverter; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.SpelMessages; + +/** + * A method resolver that uses reflection to locate the method that should be invoked + * + * @author Andy Clement + * @since 3.0 + */ +public class ReflectiveMethodResolver implements MethodResolver { + + /** + * Locate a method on a type. There are three kinds of match that might occur: + *
    + *
  1. An exact match where the types of the arguments match the types of the constructor + *
  2. An in-exact match where the types we are looking for are subtypes of those defined on the constructor + *
  3. A match where we are able to convert the arguments into those expected by the constructor, according to the + * registered type converter. + *
+ */ + public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class[] argumentTypes) throws AccessException { + try { + TypeConverter typeConverter = context.getTypeConverter(); + Class type = (targetObject instanceof Class ? (Class) targetObject : targetObject.getClass()); + Method[] methods = type.getMethods(); + Method closeMatch = null; + int[] argsToConvert = null; + boolean multipleOptions = false; + Method matchRequiringConversion = null; + for (Method method : methods) { + if (method.isBridge()) { + continue; + } + if (method.getName().equals(name)) { + ReflectionHelper.ArgumentsMatchInfo matchInfo = null; + if (method.isVarArgs() && argumentTypes.length >= (method.getParameterTypes().length - 1)) { + // *sigh* complicated + matchInfo = ReflectionHelper.compareArgumentsVarargs(method.getParameterTypes(), argumentTypes, typeConverter); + } + else if (method.getParameterTypes().length == argumentTypes.length) { + // name and parameter number match, check the arguments + matchInfo = ReflectionHelper.compareArguments(method.getParameterTypes(), argumentTypes, typeConverter); + } + if (matchInfo != null) { + if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.EXACT) { + return new ReflectiveMethodExecutor(method, null); + } + else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.CLOSE) { + 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 SpelException(SpelMessages.MULTIPLE_POSSIBLE_METHODS, name); + } + return new ReflectiveMethodExecutor(matchRequiringConversion, argsToConvert); + } + else { + return null; + } + } + catch (EvaluationException ex) { + throw new AccessException("Failed to resolve method", ex); + } + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyResolver.java new file mode 100644 index 00000000000..b5f9d5c5081 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyResolver.java @@ -0,0 +1,309 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.support; + +import java.lang.reflect.Array; +import java.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.expression.AccessException; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.PropertyAccessor; +import org.springframework.util.ReflectionUtils; + +/** + * Simple PropertyResolver that uses reflection to access properties for reading and writing. A property can be accessed + * if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written). This + * implementation currently follows the Resolver/Executor model (it extends CacheablePropertyAccessor) - the code that + * would be used if it were a simple property accessor is shown at the end. + * + * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 + */ +public class ReflectivePropertyResolver implements PropertyAccessor { + + private final Map readerCache = new ConcurrentHashMap(); + + private final Map writerCache = new ConcurrentHashMap(); + + + /** + * @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) { + this.readerCache.put(cacheKey, method); + return true; + } + else { + Field field = findField(name, type, target instanceof Class); + if (field != null) { + this.readerCache.put(cacheKey, field); + return true; + } + } + return false; + } + + public Object read(EvaluationContext context, Object target, String name) throws AccessException { + if (target == null) { + return null; + } + 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 Array.getLength(target); + } + + CacheKey cacheKey = new CacheKey(type, name); + Member cachedMember = this.readerCache.get(cacheKey); + + if (cachedMember == null || cachedMember instanceof Method) { + Method method = (Method) cachedMember; + if (method == null) { + method = findGetterForProperty(name, type, target instanceof Class); + if (method != null) { + cachedMember = method; + this.readerCache.put(cacheKey, cachedMember); + } + } + if (method != null) { + try { + ReflectionUtils.makeAccessible(method); + return method.invoke(target); + } + catch (Exception ex) { + throw new AccessException("Unable to access property '" + name + "' through getter", 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.readerCache.put(cacheKey, cachedMember); + } + } + if (field != null) { + try { + ReflectionUtils.makeAccessible(field); + return field.get(target); + } + 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) { + this.writerCache.put(cacheKey, method); + return true; + } + else { + Field field = findField(name, type, target instanceof Class); + if (field != null) { + this.writerCache.put(cacheKey, 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()); + + 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, newValue); + 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.readerCache.put(cacheKey, cachedMember); + } + } + if (field != null) { + try { + ReflectionUtils.makeAccessible(field); + field.set(target, newValue); + return; + } + catch (Exception ex) { + throw new AccessException("Unable to access field: " + name, ex); + } + } + } + + throw new AccessException("Neither setter nor field found for property '" + name + "'"); + } + + + /** + * 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... + StringBuilder sb = new StringBuilder(propertyName.length() + 3); + sb.append("get").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1)); + String expectedGetterName = sb.toString(); + for (Method method : ms) { + if (method.getName().equals(expectedGetterName) && method.getParameterTypes().length == 0 && + (!mustBeStatic || Modifier.isStatic(method.getModifiers()))) { + return method; + } + } + // Try "is*" method... + sb = new StringBuilder(propertyName.length() + 2); + sb.append("is").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1)); + expectedGetterName = sb.toString(); + for (Method method : ms) { + if (method.getName().equals(expectedGetterName) && 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[] ms = clazz.getMethods(); + StringBuilder sb = new StringBuilder(propertyName.length() + 3); + sb.append("set").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1)); + String setterName = sb.toString(); + for (Method method : ms) { + if (method.getParameterTypes().length == 1 && + (!mustBeStatic || Modifier.isStatic(method.getModifiers())) && + method.getName().equals(setterName)) { + 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(); // TODO use getDeclaredFields() and search up hierarchy? + for (Field field : fields) { + if (field.getName().equals(name) && (!mustBeStatic || Modifier.isStatic(field.getModifiers()))) { + return field; + } + } + return null; + } + + + 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; + } + 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(); + } + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java new file mode 100644 index 00000000000..92ad0cc9109 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java @@ -0,0 +1,176 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.expression.spel.support; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.expression.ConstructorResolver; +import org.springframework.expression.EvaluationContext; +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.util.Assert; + +/** + * Provides a default EvaluationContext implementation. + * + *

To resolved properties/methods/fields this context uses a reflection mechanism. + * + * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 + */ +public class StandardEvaluationContext implements EvaluationContext { + + private Object rootObject; + + private final Map variables = new HashMap(); + + private final Map> simpleReferencesMap = new HashMap>(); + + private final List constructorResolvers = new ArrayList(); + + private final List methodResolvers = new ArrayList(); + + private final List propertyAccessors = new ArrayList(); + + private TypeLocator typeLocator = new StandardTypeLocator(); + + private TypeComparator typeComparator = new StandardTypeComparator(); + + private TypeConverter typeConverter = new StandardTypeConverter(); + + private OperatorOverloader operatorOverloader = new StandardOperatorOverloader(); + + + public StandardEvaluationContext() { + this.methodResolvers.add(new ReflectiveMethodResolver()); + this.constructorResolvers.add(new ReflectiveConstructorResolver()); + this.propertyAccessors.add(new ReflectivePropertyResolver()); + } + + + public void setRootObject(Object rootObject) { + this.rootObject = rootObject; + } + + public Object getRootObject() { + return this.rootObject; + } + + public void setVariable(String name, Object value) { + this.variables.put(name, value); + } + + public void registerFunction(String name, Method m) { + this.variables.put(name, m); + } + + public Object lookupVariable(String name) { + return this.variables.get(name); + } + + public void addReference(String contextName, String objectName, Object value) { + Map contextMap = this.simpleReferencesMap.get(contextName); + if (contextMap == null) { + contextMap = new HashMap(); + this.simpleReferencesMap.put(contextName, contextMap); + } + contextMap.put(objectName, value); + } + + public Object lookupReference(Object contextName, String objectName) { + String contextToLookup = (contextName == null ? "root" : (String) contextName); + // if (contextName==null) return simpleReferencesMap; + Map contextMap = this.simpleReferencesMap.get(contextToLookup); + if (contextMap == null) { + return null; + } + if (objectName == null) { + return contextMap; + } + return contextMap.get(objectName); + } + + public void addConstructorResolver(ConstructorResolver resolver) { + this.constructorResolvers.add(this.constructorResolvers.size() - 1, resolver); + } + + public List getConstructorResolvers() { + return this.constructorResolvers; + } + + public void addMethodResolver(MethodResolver resolver) { + this.methodResolvers.add(this.methodResolvers.size() - 1, resolver); + } + + public List getMethodResolvers() { + return this.methodResolvers; + } + + public void addPropertyAccessor(PropertyAccessor accessor) { + this.propertyAccessors.add(this.propertyAccessors.size() - 1, accessor); + } + + public List getPropertyAccessors() { + return this.propertyAccessors; + } + + public void setTypeLocator(TypeLocator typeLocator) { + Assert.notNull(typeLocator, "TypeLocator must not be null"); + this.typeLocator = typeLocator; + } + + public TypeLocator getTypeLocator() { + return this.typeLocator; + } + + public void setTypeComparator(TypeComparator typeComparator) { + Assert.notNull(typeComparator, "TypeComparator must not be null"); + this.typeComparator = typeComparator; + } + + public TypeComparator getTypeComparator() { + return this.typeComparator; + } + + public void setTypeConverter(TypeConverter typeConverter) { + Assert.notNull(typeConverter, "TypeConverter must not be null"); + this.typeConverter = typeConverter; + } + + public TypeConverter getTypeConverter() { + return this.typeConverter; + } + + public void setOperatorOverloader(OperatorOverloader operatorOverloader) { + Assert.notNull(operatorOverloader, "OperatorOverloader must not be null"); + this.operatorOverloader = operatorOverloader; + } + + public OperatorOverloader getOperatorOverloader() { + return this.operatorOverloader; + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardOperatorOverloader.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardOperatorOverloader.java new file mode 100644 index 00000000000..0b03aa702ac --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardOperatorOverloader.java @@ -0,0 +1,38 @@ +/* + * 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"); + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardComparator.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java similarity index 84% rename from org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardComparator.java rename to org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java index 735811cf1ac..614f6b5ad26 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardComparator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.spel.standard; + +package org.springframework.expression.spel.support; import org.springframework.expression.TypeComparator; import org.springframework.expression.spel.SpelException; @@ -23,8 +24,54 @@ import org.springframework.expression.spel.SpelMessages; * 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 StandardComparator implements TypeComparator { +public class StandardTypeComparator implements TypeComparator { + + @SuppressWarnings("unchecked") + public int compare(Object left, Object right) throws SpelException { + // If one is null, check if the other is + if (left == null) { + return right == null ? 0 : 1; + } + else if (right == null) { + return left == null ? 0 : -1; + } + + // Basic number comparisons + if (left instanceof Number && right instanceof Number) { + Number leftNumber = (Number) left; + Number rightNumber = (Number) right; + if (leftNumber instanceof Double || rightNumber instanceof Double) { + Double d1 = leftNumber.doubleValue(); + Double d2 = rightNumber.doubleValue(); + return d1.compareTo(d2); + } + else if (leftNumber instanceof Float || rightNumber instanceof Float) { + Float f1 = leftNumber.floatValue(); + Float f2 = rightNumber.floatValue(); + return f1.compareTo(f2); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { + Long l1 = leftNumber.longValue(); + Long l2 = rightNumber.longValue(); + return l1.compareTo(l2); + } + else { + Integer i1 = leftNumber.intValue(); + Integer i2 = rightNumber.intValue(); + return i1.compareTo(i2); + } + } + + if (left.getClass() == right.getClass() && left instanceof Comparable) { + return ((Comparable) left).compareTo(right); + } + else { + throw new SpelException(SpelMessages.NOT_COMPARABLE, left.getClass(), right.getClass()); + } + } public boolean canCompare(Object left, Object right) { if (left == null || right == null) { @@ -39,40 +86,4 @@ public class StandardComparator implements TypeComparator { return false; } - @SuppressWarnings("unchecked") - public int compare(Object left, Object right) throws SpelException { - // If one is null, check if the other is - if (left == null) { - return right == null ? 0 : 1; - } else if (right == null) { - return left == null ? 0 : -1; - } - // Basic number comparisons - if (left instanceof Number && right instanceof Number) { - Number leftNumber = (Number) left; - Number rightNumber = (Number) right; - if (leftNumber instanceof Double || rightNumber instanceof Double) { - Double d1 = leftNumber.doubleValue(); - Double d2 = rightNumber.doubleValue(); - return d1.compareTo(d2); - } else if (leftNumber instanceof Float || rightNumber instanceof Float) { - Float f1 = leftNumber.floatValue(); - Float f2 = rightNumber.floatValue(); - return f1.compareTo(f2); - } else if (leftNumber instanceof Long || rightNumber instanceof Long) { - Long l1 = leftNumber.longValue(); - Long l2 = rightNumber.longValue(); - return l1.compareTo(l2); - } else { - Integer i1 = leftNumber.intValue(); - Integer i2 = rightNumber.intValue(); - return i1.compareTo(i2); - } - } - if (left.getClass() == right.getClass() && left instanceof Comparable) { - return ((Comparable) left).compareTo(right); - } else { - throw new SpelException(SpelMessages.NOT_COMPARABLE, left.getClass(), right.getClass()); - } - } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java new file mode 100644 index 00000000000..28304f42ce8 --- /dev/null +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java @@ -0,0 +1,87 @@ +/* + * 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.TypeConverter; +import org.springframework.expression.spel.SpelException; +import org.springframework.expression.spel.SpelMessages; +import org.springframework.util.ClassUtils; +import org.springframework.util.NumberUtils; + +/** + * @author Juergen Hoeller + * @since 3.0 + */ +public class StandardTypeConverter implements TypeConverter { + + @SuppressWarnings("unchecked") + public T convertValue(Object value, Class targetType) throws EvaluationException { + if (ClassUtils.isAssignableValue(targetType, value)) { + return (T) value; + } + if (String.class.equals(targetType)) { + return (T) (value != null ? value.toString() : null); + } + Class actualTargetType = ClassUtils.resolvePrimitiveIfNecessary(targetType); + if (Number.class.isAssignableFrom(actualTargetType)) { + try { + if (value instanceof String) { + return (T) NumberUtils.parseNumber(value.toString(), (Class) actualTargetType); + } + else if (value instanceof Number) { + return (T) NumberUtils.convertNumberToTargetClass((Number) value, (Class) actualTargetType); + } + } + catch (IllegalArgumentException ex) { + throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, ex.getMessage()); + } + } + if (Character.class.equals(actualTargetType)) { + if (value instanceof String) { + String str = (String) value; + if (str.length() == 1) { + return (T) new Character(str.charAt(0)); + } + } + else if (value instanceof Number) { + return (T) new Character((char) ((Number) value).shortValue()); + } + } + if (Boolean.class.equals(actualTargetType) && value instanceof String) { + String str = (String) value; + if ("true".equalsIgnoreCase(str)) { + return (T) Boolean.TRUE; + } + else if ("false".equalsIgnoreCase(str)) { + return (T) Boolean.FALSE; + } + } + throw new SpelException(SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), targetType); + } + + public boolean canConvert(Class sourceType, Class targetType) { + if (ClassUtils.isAssignable(targetType, sourceType) || String.class.equals(targetType)) { + return true; + } + Class actualTargetType = ClassUtils.resolvePrimitiveIfNecessary(targetType); + return (((Number.class.isAssignableFrom(actualTargetType) || Character.class.equals(actualTargetType)) && + (String.class.equals(sourceType) || Number.class.isAssignableFrom(sourceType))) || + (Boolean.class.equals(actualTargetType) && String.class.equals(sourceType))); + } + +} diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeLocator.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java similarity index 71% rename from org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeLocator.java rename to org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java index 5e4d89ab585..8f9be1b8ecd 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/standard/StandardTypeLocator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,37 +13,48 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.spel.standard; + +package org.springframework.expression.spel.support; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypeLocator; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; +import org.springframework.util.ClassUtils; /** * A default implementation of a TypeLocator that uses the context classloader (or any classloader set upon it). It * supports 'well known' packages so if a type cannot be found it will try the registered imports to locate it. * * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 */ public class StandardTypeLocator implements TypeLocator { private ClassLoader loader; + private final List knownPackagePrefixes = new ArrayList(); + public StandardTypeLocator() { - loader = Thread.currentThread().getContextClassLoader(); + this(ClassUtils.getDefaultClassLoader()); + } + + public StandardTypeLocator(ClassLoader loader) { + this.loader = loader; registerImport("java.lang"); registerImport("java.util"); } + /** * Find a (possibly unqualified) type reference - first using the typename as is, then trying any registered * prefixes if the typename cannot be found. - * * @param typename the type to locate * @return the class object for the type * @throws EvaluationException if the type cannot be found @@ -51,17 +62,17 @@ public class StandardTypeLocator implements TypeLocator { public Class findType(String typename) throws EvaluationException { String nameToLookup = typename; try { - Class c = loader.loadClass(nameToLookup); - return c; - } catch (ClassNotFoundException e) { + return this.loader.loadClass(nameToLookup); + } + catch (ClassNotFoundException ey) { // try any registered prefixes before giving up } - for (String prefix : knownPackagePrefixes) { + for (String prefix : this.knownPackagePrefixes) { try { nameToLookup = new StringBuilder().append(prefix).append(".").append(typename).toString(); - Class clazz = loader.loadClass(nameToLookup); - return clazz; - } catch (ClassNotFoundException e) { + return this.loader.loadClass(nameToLookup); + } + catch (ClassNotFoundException ex) { // might be a different prefix } } @@ -69,40 +80,20 @@ public class StandardTypeLocator implements TypeLocator { } /** - * Register a new import prefix that will be used when searching for unqualified types. Expected format is something - * like "java.lang". - * + * Register a new import prefix that will be used when searching for unqualified types. + * Expected format is something like "java.lang". * @param prefix the prefix to register */ public void registerImport(String prefix) { - knownPackagePrefixes.add(prefix); - } - - /** - * Unregister an import prefix. - * - * @param prefix the prefix to unregister - */ - public void unregisterImport(String prefix) { - knownPackagePrefixes.add(prefix); + this.knownPackagePrefixes.add(prefix); } /** * Return a list of all the import prefixes registered with this StandardTypeLocator. - * * @return list of registered import prefixes */ public List getImportPrefixes() { - return knownPackagePrefixes; - } - - /** - * Set the classloader that should be used (otherwise the context class loader will be used). - * - * @param loader the classloader to use from now on - */ - public void setClassLoader(ClassLoader loader) { - this.loader = loader; + return Collections.unmodifiableList(this.knownPackagePrefixes); } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/common/CompositeStringExpressionTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/common/CompositeStringExpressionTests.java deleted file mode 100644 index 497c73da143..00000000000 --- a/org.springframework.expression/src/test/java/org/springframework/expression/common/CompositeStringExpressionTests.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.common; - -import junit.framework.TestCase; - -import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; -import org.springframework.expression.spel.SpelExpressionParser; -import org.springframework.expression.spel.standard.StandardEvaluationContext; - -/** - * Test LiteralExpression - * - * @author Andy Clement - */ -public class CompositeStringExpressionTests extends TestCase { - - public void testGetValue() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); - Expression ex = parser.parseExpression("hello ${'world'}", DefaultTemplateParserContext.INSTANCE); - checkString("hello world", ex.getValue()); - checkString("hello world", ex.getValue(String.class)); - EvaluationContext ctx = new StandardEvaluationContext(); - checkString("hello world", ex.getValue(ctx)); - checkString("hello world", ex.getValue(ctx, String.class)); - assertEquals("hello ${'world'}", ex.getExpressionString()); - assertFalse(ex.isWritable(new StandardEvaluationContext())); - } - - // public void testSetValue() { - // try { - // LiteralExpression lEx = new LiteralExpression("somevalue"); - // lEx.setValue(new StandardEvaluationContext(), "flibble"); - // fail("Should have got an exception that the value cannot be set"); - // } catch (EvaluationException ee) { - // // success, not allowed - whilst here, check the expression value in the exception - // assertEquals(ee.getExpressionString(), "somevalue"); - // } - // } - // - // public void testGetValueType() throws Exception { - // LiteralExpression lEx = new LiteralExpression("somevalue"); - // assertEquals(String.class, lEx.getValueType()); - // assertEquals(String.class, lEx.getValueType(new StandardEvaluationContext())); - // } - - private void checkString(String expectedString, Object value) { - if (!(value instanceof String)) { - fail("Result was not a string, it was of type " + value.getClass() + " (value=" + value + ")"); - } - if (!((String) value).equals(expectedString)) { - fail("Did not get expected result. Should have been '" + expectedString + "' but was '" + value + "'"); - } - } -} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/AllTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/AllTests.java deleted file mode 100644 index 0ee87856053..00000000000 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/AllTests.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel; - -import junit.framework.Test; -import junit.framework.TestSuite; - -import org.springframework.expression.common.CompositeStringExpressionTests; -import org.springframework.expression.common.LiteralExpressionTests; - -/** - * Pulls together all the tests for Spring EL into a single suite. - * - * @author Andy Clement - * - */ -public class AllTests { - - public static Test suite() { - TestSuite suite = new TestSuite("Spring Expression Language tests"); - // $JUnit-BEGIN$ - suite.addTestSuite(BooleanExpressionTests.class); - suite.addTestSuite(LiteralTests.class); - suite.addTestSuite(ParsingTests.class); - suite.addTestSuite(VariableAndFunctionTests.class); - suite.addTestSuite(ParserErrorMessagesTests.class); - suite.addTestSuite(EvaluationTests.class); - suite.addTestSuite(OperatorTests.class); - suite.addTestSuite(ConstructorInvocationTests.class); - suite.addTestSuite(MethodInvocationTests.class); - suite.addTestSuite(PropertyAccessTests.class); - suite.addTestSuite(TypeReferencing.class); - suite.addTestSuite(PerformanceTests.class); - suite.addTestSuite(DefaultComparatorUnitTests.class); - suite.addTestSuite(TemplateExpressionParsingTests.class); - suite.addTestSuite(ExpressionLanguageScenarioTests.class); - suite.addTestSuite(ScenariosForSpringSecurity.class); - suite.addTestSuite(MapAccessTests.class); - suite.addTestSuite(SpelUtilitiesTests.class); - suite.addTestSuite(LiteralExpressionTests.class); - suite.addTestSuite(CompositeStringExpressionTests.class); - // $JUnit-END$ - return suite; - } - -} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java index 5e5d51f91f5..f6ddbeb4362 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/BooleanExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; /** diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java index 3a8dc2f5fd0..dbd8c1ece31 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.spel; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +package org.springframework.expression.spel; /** * Tests invocation of constructors. @@ -93,20 +92,4 @@ public class ConstructorInvocationTests extends ExpressionTestCase { evaluate("new String(3.0d)", "3.0", String.class); } - public void testVarargsInvocation01() throws Exception { - // Calling 'public TestCode(String... strings)' - SpelExpressionParser parser = new SpelExpressionParser(); - StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.setClasspath("target/test-classes/testcode.jar"); - - @SuppressWarnings("unused") - Object v = parser.parseExpression("new TestType('a','b','c')").getValue(ctx); - v = parser.parseExpression("new TestType('a')").getValue(ctx); - v = parser.parseExpression("new TestType()").getValue(ctx); - v = parser.parseExpression("new TestType(1,2,3)").getValue(ctx); - v = parser.parseExpression("new TestType(1)").getValue(ctx); - v = parser.parseExpression("new TestType(1,'a',3.0d)").getValue(ctx); - // v = parser.parseExpression("new TestType(new String[]{'a','b','c'})").getValue(ctx); - } - } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java index 72cc40fc5f5..b146c52b582 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java @@ -19,7 +19,7 @@ import junit.framework.TestCase; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypeComparator; -import org.springframework.expression.spel.standard.StandardComparator; +import org.springframework.expression.spel.support.StandardTypeComparator; /** * Unit tests for type comparison @@ -29,7 +29,7 @@ import org.springframework.expression.spel.standard.StandardComparator; public class DefaultComparatorUnitTests extends TestCase { public void testPrimitives() throws EvaluationException { - TypeComparator comparator = new StandardComparator(); + TypeComparator comparator = new StandardTypeComparator(); // primitive int assertTrue(comparator.compare(1, 2) < 0); assertTrue(comparator.compare(1, 1) == 0); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index 34783e664b9..8ccad12033d 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; /** @@ -325,12 +326,6 @@ public class EvaluationTests extends ExpressionTestCase { evaluateAndCheckError("'hello'?1:2", SpelMessages.TYPE_CONVERSION_ERROR); // cannot convert String to boolean } - public void testTernaryOperator04() { - // an int becomes TRUE if not 0, otherwise FALSE - evaluate("12?1:2", 1, Integer.class); // int to boolean - evaluate("1L?1:2", 1, Integer.class); // long to boolean - } - // Indexer // public void testCutProcessor01() { // evaluate("{1,2,3,4,5}.cut(1,3)", "[2, 3, 4]", ArrayList.class); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionLanguageScenarioTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionLanguageScenarioTests.java index 95252a56f42..8da7803ee84 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionLanguageScenarioTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionLanguageScenarioTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; -import java.awt.Color; +import java.awt.*; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -23,16 +24,13 @@ import java.util.List; import java.util.Map; import org.springframework.expression.AccessException; -import org.springframework.expression.CacheablePropertyAccessor; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.ParseException; import org.springframework.expression.PropertyAccessor; -import org.springframework.expression.PropertyReaderExecutor; -import org.springframework.expression.PropertyWriterExecutor; -import org.springframework.expression.spel.standard.StandardEvaluationContext; -import org.springframework.expression.spel.standard.StandardIndividualTypeConverter; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Testcases showing the common scenarios/use-cases for picking up the expression language support. @@ -63,7 +61,7 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase { public void testScenario_UsingStandardInfrastructure() { try { // Create a parser - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); // Parse an expression Expression expr = parser.parseExpression("new String('hello world')"); // Evaluate it using a 'standard' context @@ -82,43 +80,12 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase { } } - /** - * Scenario: using the standard context but adding a jar to the classpath and registering an import. - */ - public void testScenario_LoadingDifferentClassesAndUsingImports() { - try { - // Create a parser - SpelExpressionParser parser = new SpelExpressionParser(); - // Use the standard evaluation context - StandardEvaluationContext ctx = new StandardEvaluationContext(); - // Set the classpath (creates a new classloader with this classpath and uses it) - ctx.setClasspath("target/test-classes/testcode.jar"); - // Register an import (so types in a.b.c can be referred to by their short name) - ctx.registerImport("a.b.c"); - - // Parse an expression (here, PackagedType is in package a.b.c) - Expression expr = parser.parseExpression("new PackagedType().sayHi('Andy')"); - - // Evaluate the expression in our context - Object value = expr.getValue(ctx); - - assertEquals("Hi! Andy", value); - assertEquals(String.class, value.getClass()); - } catch (EvaluationException ee) { - ee.printStackTrace(); - fail("Unexpected Exception: " + ee.getMessage()); - } catch (ParseException pe) { - pe.printStackTrace(); - fail("Unexpected Exception: " + pe.getMessage()); - } - } - /** * Scenario: using the standard context but adding your own variables */ public void testScenario_DefiningVariablesThatWillBeAccessibleInExpressions() throws Exception { // Create a parser - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); // Use the standard evaluation context StandardEvaluationContext ctx = new StandardEvaluationContext(); ctx.setVariable("favouriteColour","blue"); @@ -153,22 +120,21 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase { */ public void testScenario_UsingADifferentRootContextObject() throws Exception { // Create a parser - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); // Use the standard evaluation context StandardEvaluationContext ctx = new StandardEvaluationContext(); TestClass tc = new TestClass(); tc.setProperty(42); tc.str = "wibble"; - ctx.setRootObject(tc); // read it, set it, read it again Expression expr = parser.parseExpression("str"); Object value = expr.getValue(ctx); assertEquals("wibble", value); - expr = parser.parseExpression("str"); - expr.setValue(ctx,"wobble"); + expr = parser.parseExpression("str"); + expr.setValue(ctx, "wobble"); expr = parser.parseExpression("str"); value = expr.getValue(ctx); assertEquals("wobble", value); @@ -200,7 +166,7 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase { public void testScenario_RegisteringJavaMethodsAsFunctionsAndCallingThem() throws SecurityException, NoSuchMethodException { try { // Create a parser - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); // Use the standard evaluation context StandardEvaluationContext ctx = new StandardEvaluationContext(); ctx.registerFunction("repeat",ExpressionLanguageScenarioTests.class.getDeclaredMethod("repeat",String.class)); @@ -221,40 +187,52 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase { /** * Scenario: add a property resolver that will get called in the resolver chain, this one only supports reading. */ - public void testScenario_AddingYourOwnPropertyResolvers_1() throws SecurityException, NoSuchMethodException { - try { - // Create a parser - SpelExpressionParser parser = new SpelExpressionParser(); - // Use the standard evaluation context - StandardEvaluationContext ctx = new StandardEvaluationContext(); - - ctx.addPropertyAccessor(new FruitColourAccessor()); - Expression expr = parser.parseExpression("orange"); - Object value = expr.getValue(ctx); - assertEquals(Color.orange,value); - - try { - expr.setValue(ctx,Color.blue); - fail("Should not be allowed to set oranges to be blue !"); - } catch (EvaluationException ee) { - SpelException ele = (SpelException)ee; - assertEquals(ele.getMessageUnformatted(),SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND); - } + public void testScenario_AddingYourOwnPropertyResolvers_1() throws Exception { + // Create a parser + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); + // Use the standard evaluation context + StandardEvaluationContext ctx = new StandardEvaluationContext(); - } catch (EvaluationException ee) { - ee.printStackTrace(); - fail("Unexpected Exception: " + ee.getMessage()); - } catch (ParseException pe) { - pe.printStackTrace(); - fail("Unexpected Exception: " + pe.getMessage()); + ctx.addPropertyAccessor(new FruitColourAccessor()); + Expression expr = parser.parseExpression("orange"); + Object value = expr.getValue(ctx); + assertEquals(Color.orange, value); + + try { + expr.setValue(ctx, Color.blue); + fail("Should not be allowed to set oranges to be blue !"); + } + catch (SpelException ee) { + assertEquals(ee.getMessageUnformatted(), SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND); } } + public void testScenario_AddingYourOwnPropertyResolvers_2() throws Exception { + // Create a parser + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); + // Use the standard evaluation context + StandardEvaluationContext ctx = new StandardEvaluationContext(); + + ctx.addPropertyAccessor(new VegetableColourAccessor()); + Expression expr = parser.parseExpression("pea"); + Object value = expr.getValue(ctx); + assertEquals(Color.green, value); + + try { + expr.setValue(ctx, Color.blue); + fail("Should not be allowed to set peas to be blue !"); + } + catch (SpelException ee) { + assertEquals(ee.getMessageUnformatted(), SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND); + } + } + + /** * Regardless of the current context object, or root context object, this resolver can tell you what colour a fruit is ! * It only supports property reading, not writing. To support writing it would need to override canWrite() and write() */ - static class FruitColourAccessor implements PropertyAccessor { + private static class FruitColourAccessor implements PropertyAccessor { private static Map propertyMap = new HashMap(); @@ -270,69 +248,30 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase { public Class[] getSpecificTargetClasses() { return null; } - - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { + + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { return propertyMap.containsKey(name); } - - public Object read(EvaluationContext context, Object target, Object name) throws AccessException { + public Object read(EvaluationContext context, Object target, String name) throws AccessException { return propertyMap.get(name); } - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { return false; } - public void write(EvaluationContext context, Object target, Object name, Object newValue) + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { } - } - /** - * Scenario: add an optimized property resolver. Property resolution can be thought of it two parts: resolving (finding the property you mean) and accessing (reading or writing that property). - * In some cases the act of discovering which property is meant is expensive - and there is no benefit to rediscovering it every time the expression is evaluated as it will - * always be the same property. For example, with reflection it can be expensive to find out which field on an object maps to the property, but it will always be the same field - * for each evaluation. In these cases we use a Resolver/Executor based property accessor. In this setup the property resolver does not immediately return the value of the property, - * instead it returns an executor object that can be used to read the property. The executor can be cached and reused by SPEL so it does not go back to the resolver every time the - * expression is evaluated. In this testcase we use this different accessor mechanism to return the colours of vegetables. - */ - public void testScenario_AddingYourOwnPropertyResolvers_2() throws SecurityException, NoSuchMethodException { - try { - // Create a parser - SpelExpressionParser parser = new SpelExpressionParser(); - // Use the standard evaluation context - StandardEvaluationContext ctx = new StandardEvaluationContext(); - - ctx.addPropertyAccessor(new VegetableColourAccessor()); - Expression expr = parser.parseExpression("pea"); - Object value = expr.getValue(ctx); - assertEquals(Color.green,value); - - try { - expr.setValue(ctx,Color.blue); - fail("Should not be allowed to set peas to be blue !"); - } catch (EvaluationException ee) { - SpelException ele = (SpelException)ee; - assertEquals(ele.getMessageUnformatted(),SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND); - } - - } catch (EvaluationException ee) { - ee.printStackTrace(); - fail("Unexpected Exception: " + ee.getMessage()); - } catch (ParseException pe) { - pe.printStackTrace(); - fail("Unexpected Exception: " + pe.getMessage()); - } - } - /** * Regardless of the current context object, or root context object, this resolver can tell you what colour a vegetable is ! * It only supports property reading, not writing. */ - static class VegetableColourAccessor extends CacheablePropertyAccessor { + private static class VegetableColourAccessor implements PropertyAccessor { private static Map propertyMap = new HashMap(); @@ -348,84 +287,20 @@ public class ExpressionLanguageScenarioTests extends ExpressionTestCase { return null; } - /** - * Work out if we can resolve the named property and if so return an executor that can be cached and reused to - * discover the value. - */ - public PropertyReaderExecutor getReaderAccessor(EvaluationContext relatedContext, Object target, Object name) { - if (propertyMap.containsKey(name)) { - return new VegetableColourExecutor(propertyMap.get(name)); - } - return null; + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return propertyMap.containsKey(name); } - public PropertyWriterExecutor getWriterAccessor(EvaluationContext context, Object target, Object name) { - return null; - } - - } - - static class VegetableColourExecutor implements PropertyReaderExecutor { - private Color colour; - - public VegetableColourExecutor(Color colour) { - this.colour = colour; + public Object read(EvaluationContext context, Object target, String name) throws AccessException { + return propertyMap.get(name); } - public Object execute(EvaluationContext context, Object target) throws AccessException { - return colour; + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return false; } - - } - - /** - * Scenario: adding your own type converter - */ - public void testScenario_AddingYourOwnTypeConverter() throws SecurityException, NoSuchMethodException { - try { - SpelExpressionParser parser = new SpelExpressionParser(); - StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.registerFunction("functionTakesColour",ExpressionLanguageScenarioTests.class.getDeclaredMethod("functionTakesColour",Color.class)); - Expression expr = parser.parseExpression("#functionTakesColour('orange')"); - try { - @SuppressWarnings("unused") - Object value = expr.getValue(ctx); - fail("Should have failed, no type converter registered"); - } catch (EvaluationException ee) {} - - ctx.addTypeConverter(new StringToColorConverter()); - Object value = expr.getValue(ctx); - - assertEquals(Color.orange,value); - } catch (EvaluationException ee) { - ee.printStackTrace(); - fail("Unexpected Exception: " + ee.getMessage()); - } catch (ParseException pe) { - pe.printStackTrace(); - fail("Unexpected Exception: " + pe.getMessage()); + public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException { } } - - public static Color functionTakesColour(Color c) {return c;} - - static class StringToColorConverter implements StandardIndividualTypeConverter { - public Object convert(Object value) throws EvaluationException { - String colourName = (String)value; - if (colourName.equals("orange")) return Color.orange; - else if (colourName.equals("red")) return Color.red; - else return Color.blue; // hmm, quite a simplification here - } - - public Class[] getFrom() { - return new Class[]{String.class}; - } - - public Class getTo() { - return Color.class; - } - - } - } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java index feeeb02cd62..110826843c8 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import java.util.Arrays; @@ -23,7 +24,8 @@ import junit.framework.TestCase; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.ParseException; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Common superclass for expression tests. @@ -37,7 +39,7 @@ public abstract class ExpressionTestCase extends TestCase { protected final static boolean SHOULD_BE_WRITABLE = true; protected final static boolean SHOULD_NOT_BE_WRITABLE = false; - protected final static SpelExpressionParser parser = new SpelExpressionParser(); + protected final static SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); protected final static StandardEvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext(); /** @@ -49,7 +51,7 @@ public abstract class ExpressionTestCase extends TestCase { */ public void evaluate(String expression, Object expectedValue, Class expectedResultType) { try { - SpelExpression expr = parser.parseExpression(expression); + Expression expr = parser.parseExpression(expression); if (expr == null) { fail("Parser returned null for expression"); } @@ -97,7 +99,7 @@ public abstract class ExpressionTestCase extends TestCase { public void evaluateAndAskForReturnType(String expression, Object expectedValue, Class expectedResultType) { try { - SpelExpression expr = parser.parseExpression(expression); + Expression expr = parser.parseExpression(expression); if (expr == null) { fail("Parser returned null for expression"); } @@ -146,7 +148,7 @@ public abstract class ExpressionTestCase extends TestCase { public void evaluate(String expression, Object expectedValue, Class expectedClassOfResult, boolean shouldBeWritable) { try { - SpelExpression e = parser.parseExpression(expression); + Expression e = parser.parseExpression(expression); if (e == null) { fail("Parser returned null for expression"); } @@ -288,7 +290,7 @@ public abstract class ExpressionTestCase extends TestCase { */ protected void parseAndCheckError(String expression, SpelMessages expectedMessage, Object... otherProperties) { try { - SpelExpression expr = parser.parseExpression(expression); + Expression expr = parser.parseExpression(expression); SpelUtilities.printAbstractSyntaxTree(System.out, expr); fail("Parsing should have failed!"); } catch (ParseException pe) { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/common/LiteralExpressionTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralExpressionTests.java similarity index 89% rename from org.springframework.expression/src/test/java/org/springframework/expression/common/LiteralExpressionTests.java rename to org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralExpressionTests.java index 5844f03dc78..3c5ae20f339 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/common/LiteralExpressionTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.expression.common; + +package org.springframework.expression.spel; import junit.framework.TestCase; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.common.LiteralExpression; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** - * Test LiteralExpression - * * @author Andy Clement */ public class LiteralExpressionTests extends TestCase { @@ -44,7 +44,8 @@ public class LiteralExpressionTests extends TestCase { LiteralExpression lEx = new LiteralExpression("somevalue"); lEx.setValue(new StandardEvaluationContext(), "flibble"); fail("Should have got an exception that the value cannot be set"); - } catch (EvaluationException ee) { + } + catch (EvaluationException ee) { // success, not allowed - whilst here, check the expression value in the exception assertEquals(ee.getExpressionString(), "somevalue"); } @@ -64,4 +65,5 @@ public class LiteralExpressionTests extends TestCase { fail("Did not get expected result. Should have been '" + expectedString + "' but was '" + value + "'"); } } + } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java index 0d667457d96..8f534284829 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; /** @@ -78,8 +79,7 @@ public class LiteralTests extends ExpressionTestCase { // ask for the result to be made into an Integer evaluateAndAskForReturnType("0x20 * 2L", 64, Integer.class); // ask for the result to be made into an Integer knowing that it will not fit - evaluateAndCheckError("0x1220 * 0xffffffffL", Integer.class, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, -1, - "long value '19928648248800' cannot be represented as an int"); + evaluateAndCheckError("0x1220 * 0xffffffffL", Integer.class, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, -1); } public void testSignedIntLiterals() { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java index 0ed87225ac7..ddc0173ba7d 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import java.util.Map; @@ -21,7 +22,8 @@ import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.PropertyAccessor; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Testing variations on map access. @@ -30,31 +32,6 @@ import org.springframework.expression.spel.standard.StandardEvaluationContext; */ public class MapAccessTests extends ExpressionTestCase { - static class MapAccessor implements PropertyAccessor { - - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { - return (((Map) target).containsKey(name)); - } - - public Object read(EvaluationContext context, Object target, Object name) throws AccessException { - return ((Map) target).get(name); - } - - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { - return true; - } - - public void write(EvaluationContext context, Object target, Object name, Object newValue) - throws AccessException { - ((Map) target).put(name, newValue); - } - - public Class[] getSpecificTargetClasses() { - return new Class[] { Map.class }; - } - - } - public void testSimpleMapAccess01() { evaluate("testMap.get('monday')", "montag", String.class); } @@ -64,7 +41,7 @@ public class MapAccessTests extends ExpressionTestCase { } public void testCustomMapAccessor() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); StandardEvaluationContext ctx = TestScenarioCreator.getTestEvaluationContext(); ctx.addPropertyAccessor(new MapAccessor()); @@ -74,7 +51,7 @@ public class MapAccessTests extends ExpressionTestCase { } public void testVariableMapAccess() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); StandardEvaluationContext ctx = TestScenarioCreator.getTestEvaluationContext(); ctx.setVariable("day", "saturday"); @@ -83,7 +60,29 @@ public class MapAccessTests extends ExpressionTestCase { assertEquals("samstag", value); } - // public void testMapAccess04() { - // evaluate("testMap[monday]", "montag", String.class); - // } + + private static class MapAccessor implements PropertyAccessor { + + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return (((Map) target).containsKey(name)); + } + + public Object read(EvaluationContext context, Object target, String name) throws AccessException { + return ((Map) target).get(name); + } + + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return true; + } + + public void write(EvaluationContext context, Object target, String name, Object newValue) + throws AccessException { + ((Map) target).put(name, newValue); + } + + public Class[] getSpecificTargetClasses() { + return new Class[] { Map.class }; + } + } + } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java index 98ec9a696aa..be8b6ee8e7a 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/MethodInvocationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,10 +13,8 @@ * 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.spel.standard.StandardEvaluationContext; +package org.springframework.expression.spel; /** * Tests invocation of methods. @@ -88,25 +86,4 @@ public class MethodInvocationTests extends ExpressionTestCase { // evaluate("aVarargsMethod2(8,new String[]{'a','b','c'})", 11, Integer.class); } - // Due to conversion there are two possible methods to call ... - public void testVarargsInvocation03() throws Exception { - // Calling 'm(String... strings)' and 'm(int i,String... strings)' - try { - SpelExpressionParser parser = new SpelExpressionParser(); - StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.setClasspath("target/test-classes/testcode.jar"); - - Object v = null; - v = parser.parseExpression("new TestType().m(1,2,3)").getValue(ctx); - // v = parser.parseExpression("new TestType().m('a','b','c')").getValue(ctx); - // v = parser.parseExpression("new TestType().m(5,'a','b','c')").getValue(ctx); - // v = parser.parseExpression("new TestType().m()").getValue(ctx); - // v = parser.parseExpression("new TestType().m(1)").getValue(ctx); - // v = parser.parseExpression("new TestType().m(1,'a',3.0d)").getValue(ctx); - // v = parser.parseExpression("new TestType().m(new String[]{'a','b','c'})").getValue(ctx); - fail("Should have detected ambiguity, there are two possible matches"); - } catch (EvaluationException ee) { - } - } - } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java index 4d904390ad2..424768fff11 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/OperatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; /** diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java index 7c2d6ef2ad2..531fdf63278 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParserErrorMessagesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; /** diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java index 572619fe40f..641d485f3cb 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ParsingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import junit.framework.TestCase; import org.springframework.expression.ParseException; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; /** * Parse some expressions and check we get the AST we expect. Rather than inspecting each node in the AST, we ask it to @@ -27,12 +29,7 @@ import org.springframework.expression.ParseException; */ public class ParsingTests extends TestCase { - private SpelExpressionParser parser; - - @Override - public void setUp() { - parser = new SpelExpressionParser(); - } + private SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); // literals public void testLiteralBoolean01() { @@ -393,7 +390,7 @@ public class ParsingTests extends TestCase { */ public void parseCheck(String expression, String expectedStringFormOfAST) { try { - SpelExpression e = parser.parseExpression(expression); + SpelExpression e = (SpelExpression) parser.parseExpression(expression); if (e != null && !e.toStringAST().equals(expectedStringFormOfAST)) { SpelUtilities.printAbstractSyntaxTree(System.err, e); } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/PerformanceTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/PerformanceTests.java index e56d76cb26e..a0bf4473c85 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/PerformanceTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/PerformanceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import junit.framework.TestCase; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; import org.springframework.expression.spel.ast.ConstructorReference; import org.springframework.expression.spel.ast.PropertyOrFieldReference; @@ -30,35 +31,39 @@ import org.springframework.expression.spel.ast.PropertyOrFieldReference; */ public class PerformanceTests extends TestCase { - public static final int ITERATIONS = 1000; + public static final int ITERATIONS = 100000; public static final boolean report = true; - private static SpelExpressionParser parser = new SpelExpressionParser(); + private static SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); private static EvaluationContext eContext = TestScenarioCreator.getTestEvaluationContext(); - public void testPerformanceOfSimpleAccess() throws Exception { + public void testPerformanceOfPropertyAccess() throws Exception { long starttime = 0; long endtime = 0; starttime = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()"); - if (expr == null) + Expression expr = parser.parseExpression("placeOfBirth.city"); + if (expr == null) { fail("Parser returned null for expression"); + } Object value = expr.getValue(eContext); } endtime = System.currentTimeMillis(); long freshParseTime = endtime - starttime; + System.out.println(freshParseTime); - Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()"); - if (expr == null) + Expression expr = parser.parseExpression("placeOfBirth.city"); + if (expr == null) { fail("Parser returned null for expression"); + } starttime = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { Object value = expr.getValue(eContext); } endtime = System.currentTimeMillis(); long reuseTime = endtime - starttime; + System.out.println(reuseTime); if (reuseTime > freshParseTime) { System.out.println("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms"); System.out.println("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms"); @@ -66,33 +71,13 @@ public class PerformanceTests extends TestCase { } } - /** - * Testing that using a resolver/executor split for constructor invocation (ie. just doing the reflection once to - * find the constructor then executing it over and over) is faster than redoing the reflection and execution every - * time. - * - * MacBook speeds: 4-Aug-08
- * Fresh parse every time, ITERATIONS iterations = 373ms
- * Reuse SpelExpression, ITERATIONS iterations = 1ms
- * Reuse SpelExpression (caching off), ITERATIONS iterations = 188ms
- */ - public void testConstructorResolverExecutorBenefit01() throws Exception { + public void testPerformanceOfMethodAccess() throws Exception { long starttime = 0; long endtime = 0; - // warmup - for (int i = 0; i < ITERATIONS; i++) { - Expression expr = parser.parseExpression("new Integer(5)"); - if (expr == null) { - fail("Parser returned null for expression"); - } - Object value = expr.getValue(eContext); - } - - // ITERATIONS calls, parsing fresh each time starttime = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - Expression expr = parser.parseExpression("new Integer(5)"); + Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()"); if (expr == null) { fail("Parser returned null for expression"); } @@ -100,26 +85,9 @@ public class PerformanceTests extends TestCase { } endtime = System.currentTimeMillis(); long freshParseTime = endtime - starttime; + System.out.println(freshParseTime); - // ITERATIONS calls, parsing once and using cached executor - Expression expr = parser.parseExpression("new Integer(5)"); - if (expr == null) { - fail("Parser returned null for expression"); - } - try { - ConstructorReference.useCaching = false; - starttime = System.currentTimeMillis(); - for (int i = 0; i < ITERATIONS; i++) { - Object value = expr.getValue(eContext); - } - } finally { - ConstructorReference.useCaching = true; - } - endtime = System.currentTimeMillis(); - long cachingOffReuseTime = endtime - starttime; - - // ITERATIONS calls, parsing once and using cached executor - expr = parser.parseExpression("new Integer(5)"); + Expression expr = parser.parseExpression("getPlaceOfBirth().getCity()"); if (expr == null) { fail("Parser returned null for expression"); } @@ -129,167 +97,11 @@ public class PerformanceTests extends TestCase { } endtime = System.currentTimeMillis(); long reuseTime = endtime - starttime; - - if (report) { - System.out.println("Timings for constructor execution 'new Integer(5)'"); - System.out.println("Fresh parse every time, " + ITERATIONS + " iterations = " + freshParseTime + "ms"); - System.out.println("Reuse SpelExpression (caching off), " + ITERATIONS + " iterations = " - + cachingOffReuseTime + "ms"); - System.out.println("Reuse SpelExpression, " + ITERATIONS + " iterations = " + reuseTime + "ms"); - } + System.out.println(reuseTime); if (reuseTime > freshParseTime) { - fail("Should have been quicker to reuse a parsed expression!"); - } - if (reuseTime > cachingOffReuseTime) { - fail("Should have been quicker to reuse cached!"); - } - } - - /** - * Testing that using a resolver/executor split for property access is faster than redoing the reflection and - * execution every time. - * - * MacBook speeds:
- */ - public void testPropertyResolverExecutorBenefit_Reading() throws Exception { - long starttime = 0; - long endtime = 0; - - // warmup - for (int i = 0; i < ITERATIONS; i++) { - Expression expr = parser.parseExpression("getPlaceOfBirth().city"); - if (expr == null) { - fail("Parser returned null for expression"); - } - Object value = expr.getValue(eContext); - } - - // ITERATIONS calls, parsing fresh each time - starttime = System.currentTimeMillis(); - for (int i = 0; i < ITERATIONS; i++) { - Expression expr = parser.parseExpression("getPlaceOfBirth().city"); - if (expr == null) { - fail("Parser returned null for expression"); - } - Object value = expr.getValue(eContext); - } - endtime = System.currentTimeMillis(); - long freshParseTime = endtime - starttime; - - // ITERATIONS calls, parsing once and using cached executor - Expression expr = parser.parseExpression("getPlaceOfBirth().city"); - if (expr == null) { - fail("Parser returned null for expression"); - } - try { - PropertyOrFieldReference.useCaching = false; - starttime = System.currentTimeMillis(); - for (int i = 0; i < ITERATIONS; i++) { - Object value = expr.getValue(eContext); - } - } finally { - PropertyOrFieldReference.useCaching = true; - } - endtime = System.currentTimeMillis(); - long cachingOffReuseTime = endtime - starttime; - - // ITERATIONS calls, parsing once and using cached executor - expr = parser.parseExpression("getPlaceOfBirth().city"); - if (expr == null) { - fail("Parser returned null for expression"); - } - starttime = System.currentTimeMillis(); - for (int i = 0; i < ITERATIONS; i++) { - Object value = expr.getValue(eContext); - } - endtime = System.currentTimeMillis(); - long reuseTime = endtime - starttime; - if (report) { - System.out.println("Timings for property reader execution 'getPlaceOfBirth().city'"); - System.out.println("Fresh parse every time, " + ITERATIONS + " iterations = " + freshParseTime + "ms"); - System.out.println("Reuse SpelExpression (caching off), " + ITERATIONS + " iterations = " - + cachingOffReuseTime + "ms"); - System.out.println("Reuse SpelExpression, " + ITERATIONS + " iterations = " + reuseTime + "ms"); - } - if (reuseTime > freshParseTime) { - //TODO fail("Should have been quicker to reuse a parsed expression!"); - } - if (reuseTime > cachingOffReuseTime) { - //TODO fail("Should have been quicker to reuse cached!"); - } - } - - /** - * Testing that using a resolver/executor split for property writing is faster than redoing the reflection and - * execution every time. - * - * MacBook speeds:
- */ - public void testPropertyResolverExecutorBenefit_Writing() throws Exception { - long starttime = 0; - long endtime = 0; - - // warmup - for (int i = 0; i < ITERATIONS; i++) { - Expression expr = parser.parseExpression("randomField='Andy'"); - if (expr == null) { - fail("Parser returned null for expression"); - } - Object value = expr.getValue(eContext); - } - - // ITERATIONS calls, parsing fresh each time - starttime = System.currentTimeMillis(); - for (int i = 0; i < ITERATIONS; i++) { - Expression expr = parser.parseExpression("randomField='Andy'"); - if (expr == null) { - fail("Parser returned null for expression"); - } - Object value = expr.getValue(eContext); - } - endtime = System.currentTimeMillis(); - long freshParseTime = endtime - starttime; - - // ITERATIONS calls, parsing once and using cached executor - Expression expr = parser.parseExpression("randomField='Andy'"); - if (expr == null) { - fail("Parser returned null for expression"); - } - try { - PropertyOrFieldReference.useCaching = false; - starttime = System.currentTimeMillis(); - for (int i = 0; i < ITERATIONS; i++) { - Object value = expr.getValue(eContext); - } - } finally { - PropertyOrFieldReference.useCaching = true; - } - endtime = System.currentTimeMillis(); - long cachingOffReuseTime = endtime - starttime; - - // ITERATIONS calls, parsing once and using cached executor - expr = parser.parseExpression("randomField='Andy'"); - if (expr == null) { - fail("Parser returned null for expression"); - } - starttime = System.currentTimeMillis(); - for (int i = 0; i < ITERATIONS; i++) { - Object value = expr.getValue(eContext); - } - endtime = System.currentTimeMillis(); - long reuseTime = endtime - starttime; - if (report) { - System.out.println("Timings for property writing execution 'randomField='Andy''"); - System.out.println("Fresh parse every time, " + ITERATIONS + " iterations = " + freshParseTime + "ms"); - System.out.println("Reuse SpelExpression (caching off), " + ITERATIONS + " iterations = " - + cachingOffReuseTime + "ms"); - System.out.println("Reuse SpelExpression, " + ITERATIONS + " iterations = " + reuseTime + "ms"); - } - if (reuseTime > freshParseTime) { - //TODO fail("Should have been quicker to reuse a parsed expression!"); - } - if (reuseTime > cachingOffReuseTime) { - //TODO fail("Should have been quicker to reuse cached!"); + System.out.println("Fresh parse every time, ITERATIONS iterations = " + freshParseTime + "ms"); + System.out.println("Reuse SpelExpression, ITERATIONS iterations = " + reuseTime + "ms"); + fail("Should have been quicker to reuse!"); } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java index 9e1609a7fd0..def350ecd9c 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/PropertyAccessTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import org.springframework.expression.AccessException; @@ -20,7 +21,8 @@ import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; import org.springframework.expression.Expression; import org.springframework.expression.PropertyAccessor; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Tests accessing of properties. @@ -45,49 +47,9 @@ public class PropertyAccessTests extends ExpressionTestCase { evaluateAndCheckError("name.foobar", SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, 5); } - // This can resolve the property 'flibbles' on any String (very useful...) - static class StringyPropertyAccessor implements PropertyAccessor { - - int flibbles = 7; - - public Class[] getSpecificTargetClasses() { - return new Class[] { String.class }; - } - - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { - if (!(target instanceof String)) - throw new RuntimeException("Assertion Failed! target should be String"); - return (name.equals("flibbles")); - } - - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { - if (!(target instanceof String)) - throw new RuntimeException("Assertion Failed! target should be String"); - return (name.equals("flibbles")); - } - - public Object read(EvaluationContext context, Object target, Object name) throws AccessException { - if (!name.equals("flibbles")) - throw new RuntimeException("Assertion Failed! name should be flibbles"); - return flibbles; - } - - public void write(EvaluationContext context, Object target, Object name, Object newValue) - throws AccessException { - if (!name.equals("flibbles")) - throw new RuntimeException("Assertion Failed! name should be flibbles"); - try { - flibbles = (Integer) context.getTypeUtils().getTypeConverter().convertValue(newValue, Integer.class); - } catch (EvaluationException e) { - throw new AccessException("Cannot set flibbles to an object of type '" + newValue.getClass() + "'"); - } - } - - } - // Adding a new property accessor just for a particular type public void testAddingSpecificPropertyAccessor() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); StandardEvaluationContext ctx = new StandardEvaluationContext(); // Even though this property accessor is added after the reflection one, it specifically @@ -119,4 +81,44 @@ public class PropertyAccessTests extends ExpressionTestCase { } } + + // This can resolve the property 'flibbles' on any String (very useful...) + private static class StringyPropertyAccessor implements PropertyAccessor { + + int flibbles = 7; + + public Class[] getSpecificTargetClasses() { + return new Class[] { String.class }; + } + + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + if (!(target instanceof String)) + throw new RuntimeException("Assertion Failed! target should be String"); + return (name.equals("flibbles")); + } + + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + if (!(target instanceof String)) + throw new RuntimeException("Assertion Failed! target should be String"); + return (name.equals("flibbles")); + } + + public Object read(EvaluationContext context, Object target, String name) throws AccessException { + if (!name.equals("flibbles")) + throw new RuntimeException("Assertion Failed! name should be flibbles"); + return flibbles; + } + + public void write(EvaluationContext context, Object target, String name, Object newValue) + throws AccessException { + if (!name.equals("flibbles")) + throw new RuntimeException("Assertion Failed! name should be flibbles"); + try { + flibbles = context.getTypeConverter().convertValue(newValue, Integer.class); + } catch (EvaluationException e) { + throw new AccessException("Cannot set flibbles to an object of type '" + newValue.getClass() + "'"); + } + } + } + } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java index 734d4032088..dd788cfad81 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ScenariosForSpringSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import java.lang.reflect.Method; @@ -25,9 +26,9 @@ import org.springframework.expression.MethodExecutor; import org.springframework.expression.MethodResolver; import org.springframework.expression.PropertyAccessor; import org.springframework.expression.TypeConverter; -import org.springframework.expression.spel.reflection.ReflectionUtils; -import org.springframework.expression.spel.standard.StandardEvaluationContext; - +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.ReflectionHelper; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Spring Security scenarios from https://wiki.springsource.com/display/SECURITY/Spring+Security+Expression-based+Authorization @@ -36,74 +37,18 @@ import org.springframework.expression.spel.standard.StandardEvaluationContext; */ public class ScenariosForSpringSecurity extends ExpressionTestCase { - // Helper classes for the scenario: - static class Person { - - private String n; - - Person(String n) { this.n = n; } - - public String[] getRoles() { return new String[]{"NONE"}; } - - public boolean hasAnyRole(String... roles) { - if (roles==null) return true; - String[] myRoles = getRoles(); - for (int i=0;i[] getSpecificTargetClasses() { - return null; - } - - } - - static class PersonAccessor implements PropertyAccessor { - - Person activePerson; - - void setPerson(Person p) { this.activePerson = p; } - - public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException { - return name.equals("p"); - } - - public Object read(EvaluationContext context, Object target, Object name) throws AccessException { - return activePerson; - } - - public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException { - return false; - } - - public void write(EvaluationContext context, Object target, Object name, Object newValue) - throws AccessException { - } - - public Class[] getSpecificTargetClasses() { - return null; - } - - } - - public void testScenario02_ComparingNames() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); StandardEvaluationContext ctx = new StandardEvaluationContext(); ctx.addPropertyAccessor(new SecurityPrincipalAccessor()); @@ -183,11 +68,11 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase { Expression expr = parser.parseExpression("name == principal.name"); ctx.setRootObject(new Person("Andy")); - Boolean value = (Boolean)expr.getValue(ctx,Boolean.class); + Boolean value = expr.getValue(ctx,Boolean.class); assertTrue(value); ctx.setRootObject(new Person("Christian")); - value = (Boolean)expr.getValue(ctx,Boolean.class); + value = expr.getValue(ctx,Boolean.class); assertFalse(value); // (2) Or register an accessor that can understand 'p' and return the right person @@ -207,7 +92,7 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase { } public void testScenario03_Arithmetic() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); StandardEvaluationContext ctx = new StandardEvaluationContext(); // Might be better with a as a variable although it would work as a property too... @@ -218,59 +103,23 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase { ctx.setVariable("a",1.0d); // referenced as #a in the expression ctx.setRootObject(new Supervisor("Ben")); // so non-qualified references 'hasRole()' 'hasIpAddress()' are invoked against it - value = (Boolean)expr.getValue(ctx,Boolean.class); + value = expr.getValue(ctx,Boolean.class); assertTrue(value); ctx.setRootObject(new Manager("Luke")); ctx.setVariable("a",1.043d); - value = (Boolean)expr.getValue(ctx,Boolean.class); + value = expr.getValue(ctx,Boolean.class); assertFalse(value); } - static class MyMethodResolver implements MethodResolver { - - static class HasRoleExecutor implements MethodExecutor { - - TypeConverter tc; - - public HasRoleExecutor(TypeConverter typeConverter) { - this.tc = typeConverter; - } - - public Object execute(EvaluationContext context, Object target, Object... methodArguments) - throws AccessException { - try { - Method m = HasRoleExecutor.class.getMethod("hasRole",new String[]{}.getClass()); - Object[] args = ReflectionUtils.prepareArguments(tc,m,methodArguments); - return m.invoke(null,args); - } catch (Exception e) { - e.printStackTrace(); - throw new AccessException("Problem invoking hasRole"); - } - } - - public static boolean hasRole(String... strings) { - return true; - } - - } - - public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class[] arguments) - throws AccessException { - if (name.equals("hasRole")) { - return new HasRoleExecutor(context.getTypeUtils().getTypeConverter()); - } - return null; - } - - } - // Here i'm going to change which hasRole() executes and make it one of my own Java methods public void testScenario04_ControllingWhichMethodsRun() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); StandardEvaluationContext ctx = new StandardEvaluationContext(); - ctx.insertMethodResolver(0, new MyMethodResolver()); // NEEDS TO OVERRIDE THE REFLECTION ONE - SHOW REORDERING MECHANISM + ctx.setRootObject(new Supervisor("Ben")); // so non-qualified references 'hasRole()' 'hasIpAddress()' are invoked against it); + + ctx.addMethodResolver(new MyMethodResolver()); // NEEDS TO OVERRIDE THE REFLECTION ONE - SHOW REORDERING MECHANISM // Might be better with a as a variable although it would work as a property too... // Variable references using a '#' // SpelExpression expr = parser.parseExpression("(hasRole('SUPERVISOR') or (#a < 1.042)) and hasIpAddress('10.10.0.0/16')"); @@ -279,8 +128,7 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase { Boolean value = null; ctx.setVariable("a",1.0d); // referenced as #a in the expression - ctx.setRootObject(new Supervisor("Ben")); // so non-qualified references 'hasRole()' 'hasIpAddress()' are invoked against it - value = (Boolean)expr.getValue(ctx,Boolean.class); + value = expr.getValue(ctx,Boolean.class); assertTrue(value); // ctx.setRootObject(new Manager("Luke")); @@ -289,4 +137,164 @@ public class ScenariosForSpringSecurity extends ExpressionTestCase { // assertFalse(value); } + + static class Person { + + private String n; + + Person(String n) { this.n = n; } + + public String[] getRoles() { return new String[]{"NONE"}; } + + public boolean hasAnyRole(String... roles) { + if (roles==null) return true; + String[] myRoles = getRoles(); + for (int i=0;i[] getSpecificTargetClasses() { + return null; + } + } + + + static class PersonAccessor implements PropertyAccessor { + + Person activePerson; + + void setPerson(Person p) { this.activePerson = p; } + + public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException { + return name.equals("p"); + } + + public Object read(EvaluationContext context, Object target, String name) throws AccessException { + return activePerson; + } + + public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { + return false; + } + + public void write(EvaluationContext context, Object target, String name, Object newValue) + throws AccessException { + } + + public Class[] getSpecificTargetClasses() { + return null; + } + } + + + static class MyMethodResolver implements MethodResolver { + + static class HasRoleExecutor implements MethodExecutor { + + TypeConverter tc; + + public HasRoleExecutor(TypeConverter typeConverter) { + this.tc = typeConverter; + } + + public Object execute(EvaluationContext context, Object target, Object... arguments) + throws AccessException { + try { + Method m = HasRoleExecutor.class.getMethod("hasRole", String[].class); + Object[] args = arguments; + if (args != null) { + ReflectionHelper.convertArguments(m.getParameterTypes(), m.isVarArgs(), tc, args); + } + if (m.isVarArgs()) { + args = ReflectionHelper.setupArgumentsForVarargsInvocation(m.getParameterTypes(), args); + } + return m.invoke(null, args); + } + catch (Exception ex) { + throw new AccessException("Problem invoking hasRole", ex); + } + } + + public static boolean hasRole(String... strings) { + return true; + } + } + + public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class[] arguments) + throws AccessException { + if (name.equals("hasRole")) { + return new HasRoleExecutor(context.getTypeConverter()); + } + return null; + } + } + } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelUtilities.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpelUtilities.java similarity index 87% rename from org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelUtilities.java rename to org.springframework.expression/src/test/java/org/springframework/expression/spel/SpelUtilities.java index 9529e6f24b0..38341ec96ac 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelUtilities.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpelUtilities.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,27 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import java.io.PrintStream; +import org.springframework.expression.Expression; + /** * Utilities for working with Spring Expressions. * * @author Andy Clement - * */ public class SpelUtilities { /** * Output an indented representation of the expression syntax tree to the specified output stream. - * * @param printStream the output stream to print into * @param expression the expression to be displayed */ - public static void printAbstractSyntaxTree(PrintStream printStream, SpelExpression expression) { + public static void printAbstractSyntaxTree(PrintStream printStream, Expression expression) { printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST start"); - printAST(printStream, expression.getAST(), ""); + printAST(printStream, ((SpelExpression) expression).getAST(), ""); printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST end"); } @@ -52,4 +53,5 @@ public class SpelUtilities { } } } + } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpelUtilitiesTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpelUtilitiesTests.java deleted file mode 100644 index 35507fcdec7..00000000000 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SpelUtilitiesTests.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; - -import junit.framework.TestCase; - -/** - * Tests the SpelUtilities - * - * @author Andy Clement - */ -public class SpelUtilitiesTests extends TestCase { - - public void testPrintAbstractSyntaxTree01() throws Exception { - SpelExpression sEx = new SpelExpressionParser().parseExpression("1 + 2"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SpelUtilities.printAbstractSyntaxTree(new PrintStream(baos), sEx); - String theAst = baos.toString(); - System.out.println(theAst); - String[] expectedLines = new String[] { "===> Expression '1 + 2' - AST start", - "OperatorPlus value=+ children=#2", " CompoundExpression value=EXPRESSION", - " IntLiteral value=1", " CompoundExpression value=EXPRESSION", " IntLiteral value=2", - "===> Expression '1 + 2' - AST end" }; - //checkExpected(theAst, expectedLines); - } - - private static void checkExpected(String theData, String[] expectedLines) { - String[] theDataSplit = theData.split("\n"); - if (theDataSplit.length != expectedLines.length) { - System.out.println("TheData:"); - System.out.println(theData); - System.out.println("ExpectedData:\n" + expectedLines); - fail("Data incorrect, expected " + expectedLines.length + " but got " + theDataSplit.length + " lines"); - } - for (int i = 0; i < expectedLines.length; i++) { - assertEquals("Failure in comparison at line " + i, expectedLines[i], theDataSplit[i]); - } - } -} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java index 79770ff1d2c..e61c1d5b8cf 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,43 +13,77 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; -import org.springframework.expression.common.DefaultTemplateParserContext; +import org.springframework.expression.ParserContext; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** - * Test parsing of template expressions - * * @author Andy Clement + * @author Juergen Hoeller */ public class TemplateExpressionParsingTests extends ExpressionTestCase { + public static final ParserContext DEFAULT_TEMPLATE_PARSER_CONTEXT = new ParserContext() { + public String getExpressionPrefix() { + return "${"; + } + public String getExpressionSuffix() { + return "}"; + } + public boolean isTemplate() { + return true; + } + }; + + public void testParsingSimpleTemplateExpression01() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expr = parser.parseExpression("hello ${'world'}", DefaultTemplateParserContext.INSTANCE); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); + Expression expr = parser.parseExpression("hello ${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT); Object o = expr.getValue(); - System.out.println(o); assertEquals("hello world", o.toString()); } public void testParsingSimpleTemplateExpression02() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); - Expression expr = parser.parseExpression("hello ${'to'} you", DefaultTemplateParserContext.INSTANCE); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); + Expression expr = parser.parseExpression("hello ${'to'} you", DEFAULT_TEMPLATE_PARSER_CONTEXT); Object o = expr.getValue(); - System.out.println(o); assertEquals("hello to you", o.toString()); } public void testParsingSimpleTemplateExpression03() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); Expression expr = parser.parseExpression("The quick ${'brown'} fox jumped over the ${'lazy'} dog", - DefaultTemplateParserContext.INSTANCE); + DEFAULT_TEMPLATE_PARSER_CONTEXT); Object o = expr.getValue(); - System.out.println(o); assertEquals("The quick brown fox jumped over the lazy dog", o.toString()); } + public void testCompositeStringExpression() throws Exception { + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); + Expression ex = parser.parseExpression("hello ${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT); + checkString("hello world", ex.getValue()); + checkString("hello world", ex.getValue(String.class)); + EvaluationContext ctx = new StandardEvaluationContext(); + checkString("hello world", ex.getValue(ctx)); + checkString("hello world", ex.getValue(ctx, String.class)); + assertEquals("hello ${'world'}", ex.getExpressionString()); + assertFalse(ex.isWritable(new StandardEvaluationContext())); + } + + private void checkString(String expectedString, Object value) { + if (!(value instanceof String)) { + fail("Result was not a string, it was of type " + value.getClass() + " (value=" + value + ")"); + } + if (!value.equals(expectedString)) { + fail("Did not get expected result. Should have been '" + expectedString + "' but was '" + value + "'"); + } + } + // TODO need to support this case but what is the neatest way? Escape the clashing delimiters in the expression // string? // public void testParsingTemplateExpressionThatEmbedsTheDelimiters() throws Exception { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java index a9888990f3c..7ff89ade63d 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/TestScenarioCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,19 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; -import java.awt.Color; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; +import java.awt.*; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.testresources.Fruit; import org.springframework.expression.spel.testresources.Inventor; import org.springframework.expression.spel.testresources.Person; @@ -43,7 +40,6 @@ public class TestScenarioCreator { StandardEvaluationContext testContext = new StandardEvaluationContext(); setupRootContextObject(testContext); populateContextMap(testContext); - createTestClassloader(testContext); populateVariables(testContext); populateFunctions(testContext); return testContext; @@ -81,20 +77,6 @@ public class TestScenarioCreator { testContext.setVariable("answer", 42); } - /** - * Include a testcode jar on the default context classpath so that tests can lookup entries in it. - * @param testContext the test evaluation context - */ - private static void createTestClassloader(StandardEvaluationContext testContext) { - try { - ClassLoader cl = new URLClassLoader(new URL[] { new File("target/test-classes/testcode.jar").toURI() - .toURL() }, Thread.currentThread().getContextClassLoader()); - testContext.setClassLoader(cl); - } catch (MalformedURLException mue) { - mue.printStackTrace(); - } - } - /** * Create the root context object, an Inventor instance. Non-qualified property and method references will be * resolved against this context object. diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TypeReferencing.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/TypeReferencing.java deleted file mode 100644 index aa7f031e766..00000000000 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TypeReferencing.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2004-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.expression.spel; - -import org.springframework.expression.spel.standard.StandardTypeLocator; - -/** - * Tests referring to system types, custom types, unqualified types. - * - * @author Andy Clement - */ -public class TypeReferencing extends ExpressionTestCase { - - public void testFromAJar() { - evaluate("new SimpleType().sayHi('Andy')", "Hi! Andy", String.class); - } - - public void testFromAJar2() { - evaluate("new a.b.c.PackagedType().sayHi('Andy')", "Hi! Andy", String.class); - } - - public void testFromAJar3() { - evaluateAndCheckError("new PackagedType().sayHi('Andy')", SpelMessages.TYPE_NOT_FOUND); - } - - public void testFromAJar4() { - ((StandardTypeLocator) eContext.getTypeUtils().getTypeLocator()).registerImport("a.b.c"); - evaluate("new PackagedType().sayHi('Andy')", "Hi! Andy", String.class); - } - -} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java index aa2914e7ce4..15c70dadc1d 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/VariableAndFunctionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2008 the original author or authors. + * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; -import org.springframework.expression.spel.standard.StandardEvaluationContext; +import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Tests the evaluation of expressions that access variables and functions (lambda/java). @@ -53,13 +55,8 @@ public class VariableAndFunctionTests extends ExpressionTestCase { evaluate("#varargsFunctionReverseStringsAndMerge2(5,25)", "525", String.class); } - public void testCallingFunctionsIncorrectly() { - evaluateAndCheckError("#varargsFunctionReverseStringsAndMerge(new StringBuilder())", - SpelMessages.TYPE_CONVERSION_ERROR); - } - public void testCallingIllegalFunctions() throws Exception { - SpelExpressionParser parser = new SpelExpressionParser(); + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); StandardEvaluationContext ctx = new StandardEvaluationContext(); ctx.setVariable("notStatic", this.getClass().getMethod("nonStatic")); try { diff --git a/org.springframework.expression/src/test/resources/readme.txt b/org.springframework.expression/src/test/resources/readme.txt deleted file mode 100644 index 26350680df9..00000000000 --- a/org.springframework.expression/src/test/resources/readme.txt +++ /dev/null @@ -1,4 +0,0 @@ -rem Used to test jar referencing, to rebuild - -javac *.java -d . -jar -cvMf testcode.jar * diff --git a/org.springframework.expression/src/test/resources/testcode.jar b/org.springframework.expression/src/test/resources/testcode.jar deleted file mode 100644 index d384473253e..00000000000 Binary files a/org.springframework.expression/src/test/resources/testcode.jar and /dev/null differ diff --git a/org.springframework.expression/template.mf b/org.springframework.expression/template.mf index 707276c4eac..bbf32fb753e 100644 --- a/org.springframework.expression/template.mf +++ b/org.springframework.expression/template.mf @@ -3,7 +3,7 @@ Bundle-Name: Spring Expression Language Bundle-Vendor: SpringSource Bundle-ManifestVersion: 2 Import-Template: - org.springframework.core;version="[3.0.0, 3.0.1)", + org.springframework.util;version="[3.0.0, 3.0.1)", org.apache.commons.logging;version="[1.1.1, 2.0.0)", org.antlr.runtime;version="[3.0.1,4.0.0)", org.antlr.runtime.tree;version="[3.0.1,4.0.0)"