diff --git a/build.gradle b/build.gradle
index 3fdd181b0b..c77a580a06 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ buildscript {
configure(allprojects) { project ->
group = "org.springframework"
version = qualifyVersionIfNecessary(version)
-
+
// The following is a work-around until the Gradle build uses
// Ant 1.9.x by default. This is necessary to avoid the
// "Class not found: javac1.8" issue with Ant versions prior to 1.9.x
@@ -305,6 +305,9 @@ project("spring-context") {
testCompile("javax.inject:javax.inject-tck:1")
}
+ // pick up RmiInvocationWrapperRTD.xml in src/main
+ sourceSets.main.resources.srcDirs += "src/main/java"
+
test {
jvmArgs = ["-disableassertions:org.aspectj.weaver.UnresolvedType"] // SPR-7989
}
@@ -658,7 +661,7 @@ project("spring-test") {
optional(project(":spring-webmvc"))
optional(project(":spring-webmvc-portlet"), )
optional("junit:junit:${junitVersion}")
- optional("org.testng:testng:6.5.2")
+ optional("org.testng:testng:6.8.5")
optional("javax.servlet:javax.servlet-api:3.0.1")
optional("javax.servlet.jsp:jsp-api:2.1")
optional("javax.portlet:portlet-api:2.0")
@@ -965,7 +968,7 @@ configure(rootProject) {
gradleVersion = "1.6"
doLast() {
- def gradleOpts = "-XX:MaxPermSize=1024m -Xmx1024m"
+ def gradleOpts = "-XX:MaxMetaspaceSize=1024m -Xmx1024m"
def gradleBatOpts = "$gradleOpts -XX:MaxHeapSize=256m"
File wrapperFile = file("gradlew")
wrapperFile.text = wrapperFile.text.replace("DEFAULT_JVM_OPTS=",
diff --git a/gradlew b/gradlew
index a1787c628f..26c714686e 100755
--- a/gradlew
+++ b/gradlew
@@ -7,7 +7,7 @@
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-GRADLE_OPTS="-XX:MaxPermSize=1024m -Xmx1024m $GRADLE_OPTS"
+GRADLE_OPTS="-XX:MaxMetaspaceSize=1024m -Xmx1024m $GRADLE_OPTS"
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
diff --git a/gradlew.bat b/gradlew.bat
index e05bf9133f..994a483869 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -9,7 +9,7 @@
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set GRADLE_OPTS=-XX:MaxPermSize=1024m -Xmx1024m -XX:MaxHeapSize=256m %GRADLE_OPTS%
+set GRADLE_OPTS=-XX:MaxMetaspaceSize=1024m -Xmx1024m -XX:MaxHeapSize=256m %GRADLE_OPTS%
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
diff --git a/spring-aop/src/main/java/org/springframework/aop/target/CommonsPoolTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/CommonsPoolTargetSource.java
index 08f16258a1..9b44df6217 100644
--- a/spring-aop/src/main/java/org/springframework/aop/target/CommonsPoolTargetSource.java
+++ b/spring-aop/src/main/java/org/springframework/aop/target/CommonsPoolTargetSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ import org.springframework.core.Constants;
* of configuration properties that are relevant to your chosen implementation.
*
*
The {@code testOnBorrow}, {@code testOnReturn} and {@code testWhileIdle}
- * properties are explictly not mirrored because the implementation of
+ * properties are explicitly not mirrored because the implementation of
* {@code PoolableObjectFactory} used by this class does not implement
* meaningful validation. All exposed Commons Pool properties use the corresponding
* Commons Pool defaults: for example,
diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java
index 8c7d3a522b..31a5adbd52 100644
--- a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java
+++ b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java
@@ -163,4 +163,11 @@ public class AnnotatedClassCacheableService implements CacheableService
public Object multiUpdate(Object arg1) {
return arg1;
}
+
+ @Override
+ @CachePut(value="primary", key="#result.id")
+ public TestEntity putRefersToResult(TestEntity arg1) {
+ arg1.setId(Long.MIN_VALUE);
+ return arg1;
+ }
}
diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java
index 98c1da1e03..6e33a8a471 100644
--- a/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java
+++ b/spring-aspects/src/test/java/org/springframework/cache/config/CacheableService.java
@@ -70,4 +70,6 @@ public interface CacheableService {
T multiConditionalCacheAndEvict(Object arg1);
T multiUpdate(Object arg1);
+
+ TestEntity putRefersToResult(TestEntity arg1);
}
diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java
index d29d43894e..37fe772c47 100644
--- a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java
+++ b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java
@@ -171,4 +171,11 @@ public class DefaultCacheableService implements CacheableService {
public Long multiUpdate(Object arg1) {
return Long.valueOf(arg1.toString());
}
+
+ @Override
+ @CachePut(value="primary", key="#result.id")
+ public TestEntity putRefersToResult(TestEntity arg1) {
+ arg1.setId(Long.MIN_VALUE);
+ return arg1;
+ }
}
diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/TestEntity.java b/spring-aspects/src/test/java/org/springframework/cache/config/TestEntity.java
new file mode 100644
index 0000000000..4c43007158
--- /dev/null
+++ b/spring-aspects/src/test/java/org/springframework/cache/config/TestEntity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.cache.config;
+
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Simple test entity for use with caching tests.
+ *
+ * @author Michael Plšd
+ */
+public class TestEntity {
+
+ private Long id;
+
+ public Long getId() {
+ return this.id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @Override
+ public int hashCode() {
+ return ObjectUtils.nullSafeHashCode(this.id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (obj instanceof TestEntity) {
+ return ObjectUtils.nullSafeEquals(this.id, ((TestEntity) obj).id);
+ }
+ return false;
+ }
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
index 840e4b816f..7bcc5caba1 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -31,7 +31,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -469,11 +468,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
@Override
public Map getBeansWithAnnotation(Class extends Annotation> annotationType) {
- Set beanNames = new LinkedHashSet(getBeanDefinitionCount());
- beanNames.addAll(Arrays.asList(getBeanDefinitionNames()));
- beanNames.addAll(Arrays.asList(getSingletonNames()));
Map results = new LinkedHashMap();
- for (String beanName : beanNames) {
+ for (String beanName : getBeanDefinitionNames()) {
+ BeanDefinition beanDefinition = getBeanDefinition(beanName);
+ if (!beanDefinition.isAbstract() && (findAnnotationOnBean(beanName, annotationType) != null)) {
+ results.put(beanName, getBean(beanName));
+ }
+ }
+ for (String beanName : getSingletonNames()) {
if (findAnnotationOnBean(beanName, annotationType) != null) {
results.put(beanName, getBean(beanName));
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java
index f86765cb62..0cca9e8be0 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultDocumentLoader.java
@@ -91,8 +91,8 @@ public class DefaultDocumentLoader implements DocumentLoader {
factory.setNamespaceAware(namespaceAware);
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
+ factory.setFeature("http://apache.org/xml/features/validation/schema", false);
factory.setValidating(true);
-
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests-abstract.xml b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests-abstract.xml
new file mode 100644
index 0000000000..e82ccdc59b
--- /dev/null
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests-abstract.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java
index 62c85b60b2..5d7808f426 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java
@@ -26,6 +26,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
/**
@@ -38,6 +39,7 @@ public final class FactoryBeanTests {
private static final Class> CLASS = FactoryBeanTests.class;
private static final Resource RETURNS_NULL_CONTEXT = qualifiedResource(CLASS, "returnsNull.xml");
private static final Resource WITH_AUTOWIRING_CONTEXT = qualifiedResource(CLASS, "withAutowiring.xml");
+ private static final Resource ABSTRACT_CONTEXT = qualifiedResource(CLASS, "abstract.xml");
@Test
public void testFactoryBeanReturnsNull() throws Exception {
@@ -80,6 +82,20 @@ public final class FactoryBeanTests {
assertSame(gamma, beta.getGamma());
}
+ @Test
+ public void testAbstractFactoryBeanViaAnnotation() throws Exception {
+ DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
+ new XmlBeanDefinitionReader(factory).loadBeanDefinitions(ABSTRACT_CONTEXT);
+ factory.getBeansWithAnnotation(Component.class);
+ }
+
+ @Test
+ public void testAbstractFactoryBeanViaType() throws Exception {
+ DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
+ new XmlBeanDefinitionReader(factory).loadBeanDefinitions(ABSTRACT_CONTEXT);
+ factory.getBeansOfType(AbstractFactoryBean.class);
+ }
+
public static class NullReturningFactoryBean implements FactoryBean {
@@ -152,6 +168,7 @@ public final class FactoryBeanTests {
}
+ @Component
public static class BetaFactoryBean implements FactoryBean {
private Beta beta;
@@ -176,4 +193,7 @@ public final class FactoryBeanTests {
}
}
+ public abstract static class AbstractFactoryBean implements FactoryBean {
+ }
+
}
diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java
index 2480f64fac..f1e844ec02 100644
--- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java
+++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java
@@ -16,13 +16,9 @@
package org.springframework.beans.factory.support;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
@@ -46,12 +42,12 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.UrlResource;
import org.springframework.tests.Assume;
import org.springframework.tests.TestGroup;
-
import org.springframework.tests.sample.beans.GenericBean;
import org.springframework.tests.sample.beans.GenericIntegerBean;
import org.springframework.tests.sample.beans.GenericSetOfIntegerBean;
import org.springframework.tests.sample.beans.TestBean;
+import static org.junit.Assert.*;
/**
* @author Juergen Hoeller
@@ -115,7 +111,7 @@ public class BeanFactoryGenericsTests {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
RootBeanDefinition rbd = new RootBeanDefinition(GenericIntegerBean.class);
- List input = new ArrayList();
+ List input = new ArrayList();
input.add(1);
rbd.getPropertyValues().add("testBeanList", input);
@@ -655,18 +651,22 @@ public class BeanFactoryGenericsTests {
}
/**
- * Tests support for parameterized {@code factory-method} declarations such
- * as Mockito {@code mock()} method which has the following signature.
- *
- * {@code
+ * Tests support for parameterized static {@code factory-method} declarations such as
+ * Mockito's {@code mock()} method which has the following signature.
+ *
+ *
+ * {@code
* public static T mock(Class classToMock)
- * }
- *
+ * }
+ *
+ *
+ *
* See SPR-9493
+ *
* @since 3.2
*/
@Test
- public void parameterizedFactoryMethod() {
+ public void parameterizedStaticFactoryMethod() {
RootBeanDefinition rbd = new RootBeanDefinition(Mockito.class);
rbd.setFactoryMethodName("mock");
rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
@@ -678,6 +678,39 @@ public class BeanFactoryGenericsTests {
assertEquals(1, beans.size());
}
+ /**
+ * Tests support for parameterized instance {@code factory-method} declarations such
+ * as EasyMock's {@code IMocksControl.createMock()} method which has the following
+ * signature.
+ *
+ *
+ * {@code
+ * public T createMock(Class toMock)
+ * }
+ *
+ *
+ *
+ * See SPR-10411
+ *
+ * @since 4.0
+ */
+ @Test
+ public void parameterizedInstanceFactoryMethod() {
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+
+ RootBeanDefinition rbd = new RootBeanDefinition(MocksControl.class);
+ bf.registerBeanDefinition("mocksControl", rbd);
+
+ rbd = new RootBeanDefinition();
+ rbd.setFactoryBeanName("mocksControl");
+ rbd.setFactoryMethodName("createMock");
+ rbd.getConstructorArgumentValues().addGenericArgumentValue(Runnable.class);
+
+ bf.registerBeanDefinition("mock", rbd);
+
+ Map beans = bf.getBeansOfType(Runnable.class);
+ assertEquals(1, beans.size());
+ }
@SuppressWarnings("serial")
public static class NamedUrlList extends LinkedList {
@@ -722,4 +755,25 @@ public class BeanFactoryGenericsTests {
}
}
+ /**
+ * Pseudo-implementation of EasyMock's {@code MocksControl} class.
+ */
+ public static class MocksControl {
+
+ @SuppressWarnings("unchecked")
+ public T createMock(Class toMock) {
+
+ return (T) Proxy.newProxyInstance(
+ BeanFactoryGenericsTests.class.getClassLoader(),
+ new Class[] { toMock }, new InvocationHandler() {
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ throw new UnsupportedOperationException("mocked!");
+ }
+ });
+ }
+ }
+
}
diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java
index 222d3308f4..dc49cd0383 100644
--- a/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java
+++ b/spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java
@@ -42,14 +42,18 @@ import org.springframework.util.CollectionUtils;
public abstract class AbstractCachingConfiguration implements ImportAware {
protected AnnotationAttributes enableCaching;
+
protected CacheManager cacheManager;
+
protected KeyGenerator keyGenerator;
@Autowired(required=false)
private Collection cacheManagerBeans;
+
@Autowired(required=false)
private Collection cachingConfigurers;
+
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableCaching = AnnotationAttributes.fromMap(
@@ -59,6 +63,7 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
importMetadata.getClassName());
}
+
/**
* Determine which {@code CacheManager} bean to use. Prefer the result of
* {@link CachingConfigurer#cacheManager()} over any by-type matching. If none, fall
diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
index 771487a56f..2a477b9e8b 100644
--- a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
+++ b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
@@ -120,9 +120,9 @@ import org.springframework.core.Ordered;
* customizing the strategy for cache key generation, per Spring's {@link
* org.springframework.cache.interceptor.KeyGenerator KeyGenerator} SPI. Normally,
* {@code @EnableCaching} will configure Spring's
- * {@link org.springframework.cache.interceptor.DefaultKeyGenerator DefaultKeyGenerator}
+ * {@link org.springframework.cache.interceptor.SimpleKeyGenerator SimpleKeyGenerator}
* for this purpose, but when implementing {@code CachingConfigurer}, a key generator
- * must be provided explicitly. Return {@code new DefaultKeyGenerator()} from this method
+ * must be provided explicitly. Return {@code new SimpleKeyGenerator()} from this method
* if no customization is necessary. See {@link CachingConfigurer} Javadoc for further
* details.
*
diff --git a/spring-context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java b/spring-context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java
index 5092f474c7..b966fc232d 100644
--- a/spring-context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java
+++ b/spring-context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java
@@ -48,69 +48,17 @@ import org.w3c.dom.Element;
*/
class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
- /**
- * Simple, reusable class used for overriding defaults.
- *
- * @author Costin Leau
- */
- private static class Props {
-
- private String key;
- private String condition;
- private String method;
- private String[] caches = null;
-
- Props(Element root) {
- String defaultCache = root.getAttribute("cache");
- key = root.getAttribute("key");
- condition = root.getAttribute("condition");
- method = root.getAttribute(METHOD_ATTRIBUTE);
-
- if (StringUtils.hasText(defaultCache)) {
- caches = StringUtils.commaDelimitedListToStringArray(defaultCache.trim());
- }
- }
-
- T merge(Element element, ReaderContext readerCtx, T op) {
- String cache = element.getAttribute("cache");
-
- // sanity check
- String[] localCaches = caches;
- if (StringUtils.hasText(cache)) {
- localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
- } else {
- if (caches == null) {
- readerCtx.error("No cache specified specified for " + element.getNodeName(), element);
- }
- }
- op.setCacheNames(localCaches);
-
- op.setKey(getAttributeValue(element, "key", this.key));
- op.setCondition(getAttributeValue(element, "condition", this.condition));
-
- return op;
- }
-
- String merge(Element element, ReaderContext readerCtx) {
- String m = element.getAttribute(METHOD_ATTRIBUTE);
-
- if (StringUtils.hasText(m)) {
- return m.trim();
- }
- if (StringUtils.hasText(method)) {
- return method;
- }
- readerCtx.error("No method specified for " + element.getNodeName(), element);
- return null;
- }
- }
-
private static final String CACHEABLE_ELEMENT = "cacheable";
+
private static final String CACHE_EVICT_ELEMENT = "cache-evict";
+
private static final String CACHE_PUT_ELEMENT = "cache-put";
+
private static final String METHOD_ATTRIBUTE = "method";
+
private static final String DEFS_ELEMENT = "caching";
+
@Override
protected Class> getBeanClass(Element element) {
return CacheInterceptor.class;
@@ -226,4 +174,66 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
return defaultValue;
}
+
+ /**
+ * Simple, reusable class used for overriding defaults.
+ *
+ * @author Costin Leau
+ */
+ private static class Props {
+
+ private String key;
+
+ private String condition;
+
+ private String method;
+
+ private String[] caches = null;
+
+
+ Props(Element root) {
+ String defaultCache = root.getAttribute("cache");
+ key = root.getAttribute("key");
+ condition = root.getAttribute("condition");
+ method = root.getAttribute(METHOD_ATTRIBUTE);
+
+ if (StringUtils.hasText(defaultCache)) {
+ caches = StringUtils.commaDelimitedListToStringArray(defaultCache.trim());
+ }
+ }
+
+
+ T merge(Element element, ReaderContext readerCtx, T op) {
+ String cache = element.getAttribute("cache");
+
+ // sanity check
+ String[] localCaches = caches;
+ if (StringUtils.hasText(cache)) {
+ localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
+ } else {
+ if (caches == null) {
+ readerCtx.error("No cache specified specified for " + element.getNodeName(), element);
+ }
+ }
+ op.setCacheNames(localCaches);
+
+ op.setKey(getAttributeValue(element, "key", this.key));
+ op.setCondition(getAttributeValue(element, "condition", this.condition));
+
+ return op;
+ }
+
+ String merge(Element element, ReaderContext readerCtx) {
+ String m = element.getAttribute(METHOD_ATTRIBUTE);
+
+ if (StringUtils.hasText(m)) {
+ return m.trim();
+ }
+ if (StringUtils.hasText(method)) {
+ return method;
+ }
+ readerCtx.error("No method specified for " + element.getNodeName(), element);
+ return null;
+ }
+ }
}
diff --git a/spring-context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java b/spring-context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java
index 238a3d2805..a02d359a20 100644
--- a/spring-context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java
+++ b/spring-context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java
@@ -35,6 +35,7 @@ import org.w3c.dom.Element;
public class CacheNamespaceHandler extends NamespaceHandlerSupport {
static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager";
+
static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager";
static String extractCacheManager(Element element) {
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java
index 8f8fc76fd2..b0da1a4ec5 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java
@@ -19,8 +19,8 @@ package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -28,11 +28,15 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
+import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
+import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
@@ -61,23 +65,212 @@ import org.springframework.util.StringUtils;
*/
public abstract class CacheAspectSupport implements InitializingBean {
- public interface Invoker {
- Object invoke();
- }
-
protected final Log logger = LogFactory.getLog(getClass());
+
private CacheManager cacheManager;
private CacheOperationSource cacheOperationSource;
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
- private KeyGenerator keyGenerator = new DefaultKeyGenerator();
+ private KeyGenerator keyGenerator = new SimpleKeyGenerator();
private boolean initialized = false;
- private static final String CACHEABLE = "cacheable", UPDATE = "cacheupdate", EVICT = "cacheevict";
+
+ @Override
+ public void afterPropertiesSet() {
+ Assert.state(this.cacheManager != null, "'cacheManager' is required");
+ Assert.state(this.cacheOperationSource != null, "The 'cacheOperationSources' "
+ + "property is required: If there are no cacheable methods, "
+ + "then don't use a cache aspect.");
+ this.initialized = true;
+ }
+
+ /**
+ * Convenience method to return a String representation of this Method
+ * for use in logging. Can be overridden in subclasses to provide a
+ * different identifier for the given method.
+ * @param method the method we're interested in
+ * @param targetClass class the method is on
+ * @return log message identifying this method
+ * @see org.springframework.util.ClassUtils#getQualifiedMethodName
+ */
+ protected String methodIdentification(Method method, Class> targetClass) {
+ Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
+ return ClassUtils.getQualifiedMethodName(specificMethod);
+ }
+
+ protected Collection getCaches(CacheOperation operation) {
+ Set cacheNames = operation.getCacheNames();
+ Collection caches = new ArrayList(cacheNames.size());
+ for (String cacheName : cacheNames) {
+ Cache cache = this.cacheManager.getCache(cacheName);
+ Assert.notNull(cache, "Cannot find cache named [" + cacheName + "] for " + operation);
+ caches.add(cache);
+ }
+ return caches;
+ }
+
+ protected CacheOperationContext getOperationContext(CacheOperation operation,
+ Method method, Object[] args, Object target, Class> targetClass) {
+ return new CacheOperationContext(operation, method, args, target, targetClass);
+ }
+
+ protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
+
+ // check whether aspect is enabled
+ // to cope with cases where the AJ is pulled in automatically
+ if (this.initialized) {
+ Class> targetClass = getTargetClass(target);
+ Collection operations = getCacheOperationSource().
+ getCacheOperations(method, targetClass);
+ if (!CollectionUtils.isEmpty(operations)) {
+ return execute(invoker, new CacheOperationContexts(operations,
+ method, args, target, targetClass));
+ }
+ }
+
+ return invoker.invoke();
+ }
+
+ private Class> getTargetClass(Object target) {
+ Class> targetClass = AopProxyUtils.ultimateTargetClass(target);
+ if (targetClass == null && target != null) {
+ targetClass = target.getClass();
+ }
+ return targetClass;
+ }
+
+ private Object execute(Invoker invoker, CacheOperationContexts contexts) {
+
+ // Process any early evictions
+ processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.NO_RESULT);
+
+ // Collect puts from any @Cachable miss
+ List cachePutRequests = new ArrayList();
+ collectPutRequests(contexts.get(CacheableOperation.class),
+ ExpressionEvaluator.NO_RESULT, cachePutRequests, true);
+
+ ValueWrapper result = null;
+
+ // We only attempt to get a cached result if there are no put requests
+ if(cachePutRequests.isEmpty() && contexts.get(CachePutOperation.class).isEmpty()) {
+ result = findCachedResult(contexts.get(CacheableOperation.class));
+ }
+
+ // Invoke the method if don't have a cache hit
+ if(result == null) {
+ result = new SimpleValueWrapper(invoker.invoke());
+ }
+
+ // Collect any explicit @CachePuts
+ collectPutRequests(contexts.get(CachePutOperation.class), result.get(),
+ cachePutRequests, false);
+
+ // Process any collected put requests, either from @CachePut or a @Cacheable miss
+ for (CachePutRequest cachePutRequest : cachePutRequests) {
+ cachePutRequest.apply(result.get());
+ }
+
+ // Process any late evictions
+ processCacheEvicts(contexts.get(CacheEvictOperation.class), false, result.get());
+
+ return result.get();
+ }
+
+ private void processCacheEvicts(Collection contexts,
+ boolean beforeInvocation, Object result) {
+ for (CacheOperationContext context : contexts) {
+ CacheEvictOperation operation = (CacheEvictOperation) context.operation;
+ if (beforeInvocation == operation.isBeforeInvocation() &&
+ isConditionPassing(context, result)) {
+ performCacheEvict(context, operation, result);
+ }
+ }
+ }
+
+ private void performCacheEvict(CacheOperationContext context,
+ CacheEvictOperation operation, Object result) {
+ Object key = null;
+ for (Cache cache : context.getCaches()) {
+ if (operation.isCacheWide()) {
+ logInvalidating(context, operation, null);
+ cache.clear();
+ } else {
+ if(key == null) {
+ key = context.generateKey(result);
+ }
+ logInvalidating(context, operation, key);
+ cache.evict(key);
+ }
+ }
+ }
+
+ private void logInvalidating(CacheOperationContext context,
+ CacheEvictOperation operation, Object key) {
+ if (this.logger.isTraceEnabled()) {
+ this.logger.trace("Invalidating " +
+ (key == null ? "entire cache" : "cache key " + key) +
+ " for operation " + operation + " on method " + context.method);
+ }
+ }
+
+ private void collectPutRequests(Collection contexts,
+ Object result, Collection putRequests, boolean whenNotInCache) {
+ for (CacheOperationContext context : contexts) {
+ if (isConditionPassing(context, result)) {
+ Object key = generateKey(context, result);
+ if (!whenNotInCache || findInCaches(context, key) == null) {
+ putRequests.add(new CachePutRequest(context, key));
+ }
+ }
+ }
+ }
+
+ private Cache.ValueWrapper findCachedResult(Collection contexts) {
+ ValueWrapper result = null;
+ for (CacheOperationContext context : contexts) {
+ if (isConditionPassing(context, ExpressionEvaluator.NO_RESULT)) {
+ if(result == null) {
+ result = findInCaches(context,
+ generateKey(context, ExpressionEvaluator.NO_RESULT));
+ }
+ }
+ }
+ return result;
+ }
+
+ private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
+ for (Cache cache : context.getCaches()) {
+ Cache.ValueWrapper wrapper = cache.get(key);
+ if (wrapper != null) {
+ return wrapper;
+ }
+ }
+ return null;
+ }
+
+ private boolean isConditionPassing(CacheOperationContext context, Object result) {
+ boolean passing = context.isConditionPassing(result);
+ if(!passing && this.logger.isTraceEnabled()) {
+ this.logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
+ }
+ return passing;
+ }
+
+ private Object generateKey(CacheOperationContext context, Object result) {
+ Object key = context.generateKey(result);
+ Assert.notNull(key, "Null key returned for cache operation (maybe you "
+ + "are using named params on classes without debug info?) "
+ + context.operation);
+ if (this.logger.isTraceEnabled()) {
+ this.logger.trace("Computed cache key " + key + " for operation " + context.operation);
+ }
+ return key;
+ }
+
/**
* Set the CacheManager that this cache aspect should delegate to.
@@ -116,7 +309,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
/**
* Set the KeyGenerator for this cache aspect.
- * Default is {@link DefaultKeyGenerator}.
+ * Default is {@link SimpleKeyGenerator}.
*/
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.keyGenerator = keyGenerator;
@@ -129,306 +322,30 @@ public abstract class CacheAspectSupport implements InitializingBean {
return this.keyGenerator;
}
- @Override
- public void afterPropertiesSet() {
- if (this.cacheManager == null) {
- throw new IllegalStateException("'cacheManager' is required");
- }
- if (this.cacheOperationSource == null) {
- throw new IllegalStateException("The 'cacheOperationSources' property is required: "
- + "If there are no cacheable methods, then don't use a cache aspect.");
- }
- this.initialized = true;
+ public interface Invoker {
+ Object invoke();
}
- /**
- * Convenience method to return a String representation of this Method
- * for use in logging. Can be overridden in subclasses to provide a
- * different identifier for the given method.
- * @param method the method we're interested in
- * @param targetClass class the method is on
- * @return log message identifying this method
- * @see org.springframework.util.ClassUtils#getQualifiedMethodName
- */
- protected String methodIdentification(Method method, Class> targetClass) {
- Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
- return ClassUtils.getQualifiedMethodName(specificMethod);
- }
- protected Collection getCaches(CacheOperation operation) {
- Set cacheNames = operation.getCacheNames();
- Collection caches = new ArrayList(cacheNames.size());
- for (String cacheName : cacheNames) {
- Cache cache = this.cacheManager.getCache(cacheName);
- if (cache == null) {
- throw new IllegalArgumentException("Cannot find cache named [" + cacheName + "] for " + operation);
- }
- caches.add(cache);
- }
- return caches;
- }
+ private class CacheOperationContexts {
- protected CacheOperationContext getOperationContext(CacheOperation operation, Method method, Object[] args,
- Object target, Class> targetClass) {
+ private final MultiValueMap, CacheOperationContext> contexts =
+ new LinkedMultiValueMap, CacheOperationContext>();
- return new CacheOperationContext(operation, method, args, target, targetClass);
- }
-
- protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
- // check whether aspect is enabled
- // to cope with cases where the AJ is pulled in automatically
- if (!this.initialized) {
- return invoker.invoke();
- }
-
- // get backing class
- Class> targetClass = AopProxyUtils.ultimateTargetClass(target);
- if (targetClass == null && target != null) {
- targetClass = target.getClass();
- }
- final Collection cacheOp = getCacheOperationSource().getCacheOperations(method, targetClass);
-
- // analyze caching information
- if (!CollectionUtils.isEmpty(cacheOp)) {
- Map> ops = createOperationContext(cacheOp, method, args, target, targetClass);
-
- // start with evictions
- inspectBeforeCacheEvicts(ops.get(EVICT));
-
- // follow up with cacheable
- CacheStatus status = inspectCacheables(ops.get(CACHEABLE));
-
- Object retVal = null;
- Map updates = inspectCacheUpdates(ops.get(UPDATE));
-
- if (status != null) {
- if (status.updateRequired) {
- updates.putAll(status.cUpdates);
- }
- // return cached object
- else {
- return status.retVal;
- }
- }
-
- retVal = invoker.invoke();
-
- inspectAfterCacheEvicts(ops.get(EVICT), retVal);
-
- if (!updates.isEmpty()) {
- update(updates, retVal);
- }
-
- return retVal;
- }
-
- return invoker.invoke();
- }
-
- private void inspectBeforeCacheEvicts(Collection evictions) {
- inspectCacheEvicts(evictions, true, ExpressionEvaluator.NO_RESULT);
- }
-
- private void inspectAfterCacheEvicts(Collection evictions,
- Object result) {
- inspectCacheEvicts(evictions, false, result);
- }
-
- private void inspectCacheEvicts(Collection evictions,
- boolean beforeInvocation, Object result) {
-
- if (!evictions.isEmpty()) {
-
- boolean log = logger.isTraceEnabled();
-
- for (CacheOperationContext context : evictions) {
- CacheEvictOperation evictOp = (CacheEvictOperation) context.operation;
-
- if (beforeInvocation == evictOp.isBeforeInvocation()) {
- if (context.isConditionPassing(result)) {
- // for each cache
- // lazy key initialization
- Object key = null;
-
- for (Cache cache : context.getCaches()) {
- // cache-wide flush
- if (evictOp.isCacheWide()) {
- cache.clear();
- if (log) {
- logger.trace("Invalidating entire cache for operation " + evictOp + " on method " + context.method);
- }
- } else {
- // check key
- if (key == null) {
- key = context.generateKey();
- }
- if (log) {
- logger.trace("Invalidating cache key " + key + " for operation " + evictOp + " on method " + context.method);
- }
- cache.evict(key);
- }
- }
- } else {
- if (log) {
- logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
- }
- }
- }
- }
- }
- }
-
- private CacheStatus inspectCacheables(Collection cacheables) {
- Map cUpdates = new LinkedHashMap(cacheables.size());
-
- boolean updateRequired = false;
- Object retVal = null;
-
- if (!cacheables.isEmpty()) {
- boolean log = logger.isTraceEnabled();
- boolean atLeastOnePassed = false;
-
- for (CacheOperationContext context : cacheables) {
- if (context.isConditionPassing()) {
- atLeastOnePassed = true;
- Object key = context.generateKey();
-
- if (log) {
- logger.trace("Computed cache key " + key + " for operation " + context.operation);
- }
- if (key == null) {
- throw new IllegalArgumentException(
- "Null key returned for cache operation (maybe you are using named params on classes without debug info?) "
- + context.operation);
- }
-
- // add op/key (in case an update is discovered later on)
- cUpdates.put(context, key);
-
- boolean localCacheHit = false;
-
- // check whether the cache needs to be inspected or not (the method will be invoked anyway)
- if (!updateRequired) {
- for (Cache cache : context.getCaches()) {
- Cache.ValueWrapper wrapper = cache.get(key);
- if (wrapper != null) {
- retVal = wrapper.get();
- localCacheHit = true;
- break;
- }
- }
- }
-
- if (!localCacheHit) {
- updateRequired = true;
- }
- }
- else {
- if (log) {
- logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
- }
- }
- }
-
- // return a status only if at least on cacheable matched
- if (atLeastOnePassed) {
- return new CacheStatus(cUpdates, updateRequired, retVal);
+ public CacheOperationContexts(Collection extends CacheOperation> operations,
+ Method method, Object[] args, Object target, Class> targetClass) {
+ for (CacheOperation operation : operations) {
+ this.contexts.add(operation.getClass(), new CacheOperationContext(operation,
+ method, args, target, targetClass));
}
}
- return null;
- }
-
- private static class CacheStatus {
- // caches/key
- final Map cUpdates;
- final boolean updateRequired;
- final Object retVal;
-
- CacheStatus(Map cUpdates, boolean updateRequired, Object retVal) {
- this.cUpdates = cUpdates;
- this.updateRequired = updateRequired;
- this.retVal = retVal;
+ public Collection get(Class extends CacheOperation> operationClass) {
+ return this.contexts.getOrDefault(operationClass, Collections. emptyList());
}
}
- private Map inspectCacheUpdates(Collection updates) {
-
- Map cUpdates = new LinkedHashMap(updates.size());
-
- if (!updates.isEmpty()) {
- boolean log = logger.isTraceEnabled();
-
- for (CacheOperationContext context : updates) {
- if (context.isConditionPassing()) {
-
- Object key = context.generateKey();
-
- if (log) {
- logger.trace("Computed cache key " + key + " for operation " + context.operation);
- }
- if (key == null) {
- throw new IllegalArgumentException(
- "Null key returned for cache operation (maybe you are using named params on classes without debug info?) "
- + context.operation);
- }
-
- // add op/key (in case an update is discovered later on)
- cUpdates.put(context, key);
- }
- else {
- if (log) {
- logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
- }
- }
- }
- }
-
- return cUpdates;
- }
-
- private void update(Map updates, Object retVal) {
- for (Map.Entry entry : updates.entrySet()) {
- CacheOperationContext operationContext = entry.getKey();
- if(operationContext.canPutToCache(retVal)) {
- for (Cache cache : operationContext.getCaches()) {
- cache.put(entry.getValue(), retVal);
- }
- }
- }
- }
-
- private Map> createOperationContext(Collection cacheOp,
- Method method, Object[] args, Object target, Class> targetClass) {
- Map> map = new LinkedHashMap>(3);
-
- Collection cacheables = new ArrayList();
- Collection evicts = new ArrayList();
- Collection updates = new ArrayList();
-
- for (CacheOperation cacheOperation : cacheOp) {
- CacheOperationContext opContext = getOperationContext(cacheOperation, method, args, target, targetClass);
-
- if (cacheOperation instanceof CacheableOperation) {
- cacheables.add(opContext);
- }
-
- if (cacheOperation instanceof CacheEvictOperation) {
- evicts.add(opContext);
- }
-
- if (cacheOperation instanceof CachePutOperation) {
- updates.add(opContext);
- }
- }
-
- map.put(CACHEABLE, cacheables);
- map.put(EVICT, evicts);
- map.put(UPDATE, updates);
-
- return map;
- }
protected class CacheOperationContext {
@@ -444,6 +361,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
private final Collection caches;
+
public CacheOperationContext(CacheOperation operation, Method method, Object[] args, Object target, Class> targetClass) {
this.operation = operation;
this.method = method;
@@ -453,14 +371,10 @@ public abstract class CacheAspectSupport implements InitializingBean {
this.caches = CacheAspectSupport.this.getCaches(operation);
}
- protected boolean isConditionPassing() {
- return isConditionPassing(ExpressionEvaluator.NO_RESULT);
- }
-
protected boolean isConditionPassing(Object result) {
if (StringUtils.hasText(this.operation.getCondition())) {
EvaluationContext evaluationContext = createEvaluationContext(result);
- return evaluator.condition(this.operation.getCondition(), this.method,
+ return CacheAspectSupport.this.evaluator.condition(this.operation.getCondition(), this.method,
evaluationContext);
}
return true;
@@ -476,7 +390,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
}
if(StringUtils.hasText(unless)) {
EvaluationContext evaluationContext = createEvaluationContext(value);
- return !evaluator.unless(unless, this.method, evaluationContext);
+ return !CacheAspectSupport.this.evaluator.unless(unless, this.method, evaluationContext);
}
return true;
}
@@ -485,16 +399,16 @@ public abstract class CacheAspectSupport implements InitializingBean {
* Computes the key for the given caching operation.
* @return generated key (null if none can be generated)
*/
- protected Object generateKey() {
+ protected Object generateKey(Object result) {
if (StringUtils.hasText(this.operation.getKey())) {
- EvaluationContext evaluationContext = createEvaluationContext(ExpressionEvaluator.NO_RESULT);
- return evaluator.key(this.operation.getKey(), this.method, evaluationContext);
+ EvaluationContext evaluationContext = createEvaluationContext(result);
+ return CacheAspectSupport.this.evaluator.key(this.operation.getKey(), this.method, evaluationContext);
}
- return keyGenerator.generate(this.target, this.method, this.args);
+ return CacheAspectSupport.this.keyGenerator.generate(this.target, this.method, this.args);
}
private EvaluationContext createEvaluationContext(Object result) {
- return evaluator.createEvaluationContext(this.caches, this.method, this.args,
+ return CacheAspectSupport.this.evaluator.createEvaluationContext(this.caches, this.method, this.args,
this.target, this.targetClass, result);
}
@@ -502,4 +416,25 @@ public abstract class CacheAspectSupport implements InitializingBean {
return this.caches;
}
}
+
+
+ private static class CachePutRequest {
+
+ private final CacheOperationContext context;
+
+ private final Object key;
+
+ public CachePutRequest(CacheOperationContext context, Object key) {
+ this.context = context;
+ this.key = key;
+ }
+
+ public void apply(Object result) {
+ if(this.context.canPutToCache(result)) {
+ for (Cache cache : this.context.getCaches()) {
+ cache.put(this.key, result);
+ }
+ }
+ }
+ }
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
index f671c84f75..6bb937a5c5 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
@@ -25,6 +25,7 @@ package org.springframework.cache.interceptor;
public class CacheEvictOperation extends CacheOperation {
private boolean cacheWide = false;
+
private boolean beforeInvocation = false;
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java
index ad4ab9721b..b4b19bea3b 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java
@@ -41,14 +41,6 @@ import org.aopalliance.intercept.MethodInvocation;
@SuppressWarnings("serial")
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
- private static class ThrowableWrapper extends RuntimeException {
- private final Throwable original;
-
- ThrowableWrapper(Throwable original) {
- this.original = original;
- }
- }
-
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
@@ -70,4 +62,14 @@ public class CacheInterceptor extends CacheAspectSupport implements MethodInterc
throw th.original;
}
}
+
+
+ private static class ThrowableWrapper extends RuntimeException {
+ private final Throwable original;
+
+ ThrowableWrapper(Throwable original) {
+ this.original = original;
+ }
+ }
+
}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
index 46d84590e8..bff9ff175c 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
@@ -23,15 +23,18 @@ import java.util.Set;
import org.springframework.util.Assert;
/**
- * Base class implementing {@link CacheOperation}.
+ * Base class for cache operations.
*
* @author Costin Leau
*/
public abstract class CacheOperation {
private Set cacheNames = Collections.emptySet();
+
private String condition = "";
+
private String key = "";
+
private String name = "";
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java
index 67a22e2b35..8891608e4d 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java
@@ -42,8 +42,10 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
private final CacheInterceptor cachingInterceptor = new CacheInterceptor();
+
private Pointcut pointcut;
+
/**
* Set a pointcut, i.e a bean that can cause conditional invocation
* of the CacheInterceptor depending on method and attributes passed.
@@ -58,12 +60,11 @@ public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
@Override
protected Object createMainInterceptor() {
this.cachingInterceptor.afterPropertiesSet();
- if (this.pointcut != null) {
- return new DefaultPointcutAdvisor(this.pointcut, this.cachingInterceptor);
- } else {
+ if (this.pointcut == null) {
// Rely on default pointcut.
throw new UnsupportedOperationException();
}
+ return new DefaultPointcutAdvisor(this.pointcut, this.cachingInterceptor);
}
/**
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java
index 8c01c66417..a18db38b1d 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CompositeCacheOperationSource.java
@@ -35,6 +35,7 @@ public class CompositeCacheOperationSource implements CacheOperationSource, Seri
private final CacheOperationSource[] cacheOperationSources;
+
/**
* Create a new CompositeCacheOperationSource for the given sources.
* @param cacheOperationSources the CacheOperationSource instances to combine
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/DefaultKeyGenerator.java b/spring-context/src/main/java/org/springframework/cache/interceptor/DefaultKeyGenerator.java
index a37218804a..d7fae627d5 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/DefaultKeyGenerator.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/DefaultKeyGenerator.java
@@ -27,15 +27,25 @@ import org.springframework.cache.interceptor.KeyGenerator;
* Uses the constant value {@value #NULL_PARAM_KEY} for any
* {@code null} parameters given.
*
+ * NOTE: As this implementation returns only a hash of the parameters
+ * it is possible for key collisions to occur. Since Spring 4.0 the
+ * {@link SimpleKeyGenerator} is used when no explicit key generator
+ * has been defined. This class remains for applications that do not
+ * wish to migrate to the {@link SimpleKeyGenerator}.
+ *
* @author Costin Leau
* @author Chris Beams
* @since 3.1
+ * @see SimpleKeyGenerator
+ * @see org.springframework.cache.annotation.CachingConfigurer
*/
public class DefaultKeyGenerator implements KeyGenerator {
public static final int NO_PARAM_KEY = 0;
+
public static final int NULL_PARAM_KEY = 53;
+
@Override
public Object generate(Object target, Method method, Object... params) {
if (params.length == 1) {
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java b/spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java
index ba109397a2..06704c5f27 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java
@@ -42,6 +42,7 @@ class ExpressionEvaluator {
public static final Object NO_RESULT = new Object();
+
private final SpelExpressionParser parser = new SpelExpressionParser();
// shared param discoverer since it caches data internally
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/LazyParamAwareEvaluationContext.java b/spring-context/src/main/java/org/springframework/cache/interceptor/LazyParamAwareEvaluationContext.java
index 7dc1289a41..631ea6e442 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/LazyParamAwareEvaluationContext.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/LazyParamAwareEvaluationContext.java
@@ -85,14 +85,14 @@ class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
return;
}
- String mKey = toString(this.method);
- Method targetMethod = this.methodCache.get(mKey);
+ String methodKey = toString(this.method);
+ Method targetMethod = this.methodCache.get(methodKey);
if (targetMethod == null) {
targetMethod = AopUtils.getMostSpecificMethod(this.method, this.targetClass);
if (targetMethod == null) {
targetMethod = this.method;
}
- this.methodCache.put(mKey, targetMethod);
+ this.methodCache.put(methodKey, targetMethod);
}
// save arguments as indexed variables
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java
index c1cbdc0e9b..fc074d6238 100644
--- a/spring-context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/NameMatchCacheOperationSource.java
@@ -42,9 +42,11 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
*/
protected static final Log logger = LogFactory.getLog(NameMatchCacheOperationSource.class);
+
/** Keys are method names; values are TransactionAttributes */
private Map> nameMap = new LinkedHashMap>();
+
/**
* Set a name/attribute map, consisting of method names
* (e.g. "myMethod") and CacheOperation instances
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java
new file mode 100644
index 0000000000..805edbbaad
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.cache.interceptor;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * A simple key as returned from the {@link SimpleKeyGenerator}.
+ *
+ * @author Phillip Webb
+ * @since 4.0
+ * @see SimpleKeyGenerator
+ */
+public final class SimpleKey implements Serializable {
+
+ private static final long serialVersionUID = 1;
+
+ public static final SimpleKey EMPTY = new SimpleKey(new Object[] {});
+
+
+ private final Object[] params;
+
+
+ /**
+ * Create a new {@link SimpleKey} instance.
+ * @param elements the elements of the key
+ */
+ public SimpleKey(Object[] elements) {
+ Assert.notNull(elements, "Elements must not be null");
+ this.params = new Object[elements.length];
+ System.arraycopy(elements, 0, this.params, 0, elements.length);
+ }
+
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj != null && getClass() == obj.getClass()) {
+ return Arrays.equals(this.params, ((SimpleKey) obj).params);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(params);
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleKey [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
+ }
+}
diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKeyGenerator.java b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKeyGenerator.java
new file mode 100644
index 0000000000..7d6fc6f16d
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKeyGenerator.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.cache.interceptor;
+
+import java.lang.reflect.Method;
+
+/**
+ * Simple key generator. Returns the parameter itself if a single non-null value
+ * is given, otherwise returns a {@link SimpleKey} of the parameters.
+ *
+ * Unlike {@link DefaultKeyGenerator}, no collisions will occur with the keys
+ * generated by this class. The returned {@link SimpleKey} object can be safely
+ * used with a {@link org.springframework.cache.concurrent.ConcurrentMapCache},
+ * however, might not be suitable for all {@link org.springframework.cache.Cache}
+ * implementations.
+ *
+ * @author Phillip Webb
+ * @since 4.0
+ * @see SimpleKey
+ * @see DefaultKeyGenerator
+ * @see org.springframework.cache.annotation.CachingConfigurer
+ */
+public class SimpleKeyGenerator implements KeyGenerator {
+
+ @Override
+ public Object generate(Object target, Method method, Object... params) {
+ if(params.length == 0) {
+ return SimpleKey.EMPTY;
+ }
+ if(params.length == 1 && params[0] != null) {
+ return params[0];
+ }
+ return new SimpleKey(params);
+ }
+
+}
diff --git a/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java b/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java
index e90bfaca6b..5c373a2b70 100644
--- a/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java
+++ b/spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java
@@ -31,6 +31,7 @@ public class SimpleCacheManager extends AbstractCacheManager {
private Collection extends Cache> caches;
+
/**
* Specify the collection of Cache instances to use for this CacheManager.
*/
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
index b660b0355b..be17b59d6d 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java
@@ -45,12 +45,12 @@ public class AnnotatedBeanDefinitionReader {
private final BeanDefinitionRegistry registry;
- private Environment environment;
-
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
+ private ConditionEvaluator conditionEvaluator;
+
/**
* Create a new {@code AnnotatedBeanDefinitionReader} for the given registry.
@@ -79,7 +79,8 @@ public class AnnotatedBeanDefinitionReader {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
- this.environment = environment;
+ this.conditionEvaluator = new ConditionEvaluator(registry, environment,
+ null, null, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
@@ -92,12 +93,13 @@ public class AnnotatedBeanDefinitionReader {
/**
* Set the Environment to use when evaluating whether
- * {@link Profile @Profile}-annotated component classes should be registered.
+ * {@link Conditional @Conditional}-annotated component classes should be registered.
*
The default is a {@link StandardEnvironment}.
* @see #registerBean(Class, String, Class...)
*/
public void setEnvironment(Environment environment) {
- this.environment = environment;
+ this.conditionEvaluator = new ConditionEvaluator(this.registry, environment,
+ null, null, null);
}
/**
@@ -133,8 +135,7 @@ public class AnnotatedBeanDefinitionReader {
public void registerBean(Class> annotatedClass, String name, Class extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
- if (ConditionalAnnotationHelper.shouldSkip(abd, this.registry,
- this.environment, this.beanNameGenerator)) {
+ if (conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
index 00d2f9810d..89ac1c8c27 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -117,7 +117,10 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
(metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) ||
annotationType.equals("javax.annotation.ManagedBean") ||
annotationType.equals("javax.inject.Named");
- return (isStereotype && attributes != null && attributes.containsKey("value"));
+
+ return (isStereotype && attributes != null &&
+ attributes.containsKey("value") &&
+ attributes.get("value") instanceof String);
}
/**
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
index a016b88554..42f5b0ab9a 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -31,6 +31,7 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
@@ -52,7 +53,6 @@ import org.springframework.util.PatternMatchUtils;
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
- * @author Phillip Webb
* @since 2.5
* @see AnnotationConfigApplicationContext#scan
* @see org.springframework.stereotype.Component
@@ -299,10 +299,6 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* bean definition has been found for the specified name
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
- if (ConditionalAnnotationHelper.shouldSkip(beanDefinition, getRegistry(),
- getEnvironment(), this.beanNameGenerator)) {
- return false;
- }
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
index a12e9ab190..08336b34b0 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
@@ -25,12 +25,11 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
-import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
@@ -39,7 +38,6 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
-import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
@@ -88,6 +86,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
private final List excludeFilters = new LinkedList();
+ private ConditionEvaluator conditionEvaluator;
+
/**
* Create a ClassPathScanningCandidateComponentProvider with a {@link StandardEnvironment}.
@@ -159,12 +159,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
/**
* Set the Environment to use when resolving placeholders and evaluating
- * {@link Profile @Profile}-annotated component classes.
+ * {@link Conditional @Conditional}-annotated component classes.
* The default is a {@link StandardEnvironment}
* @param environment the Environment to use
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
+ this.conditionEvaluator = null;
}
@Override
@@ -172,6 +173,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
return this.environment;
}
+ /**
+ * Returns the {@link BeanDefinitionRegistry} used by this scanner or {@code null}.
+ */
+ protected BeanDefinitionRegistry getRegistry() {
+ return null;
+ }
+
/**
* Set the resource pattern to use when scanning the classpath.
* This value will be appended to each base package name.
@@ -333,17 +341,26 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
- AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
- if (!metadata.isAnnotated(Profile.class.getName())) {
- return true;
- }
- AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
- return this.environment.acceptsProfiles(profile.getStringArray("value"));
+ return isConditionMatch(metadataReader);
}
}
return false;
}
+ /**
+ * Determine whether the given class is a candidate component based on any
+ * {@code @Conditional} annotations.
+ * @param metadataReader the ASM ClassReader for the class
+ * @return whether the class qualifies as a candidate component
+ */
+ private boolean isConditionMatch(MetadataReader metadataReader) {
+ if (this.conditionEvaluator == null) {
+ this.conditionEvaluator = new ConditionEvaluator(getRegistry(),
+ getEnvironment(), null, null, getResourceLoader());
+ }
+ return !conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
+ }
+
/**
* Determine whether the given bean definition qualifies as candidate.
*
The default implementation checks whether the class is concrete
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Condition.java b/spring-context/src/main/java/org/springframework/context/annotation/Condition.java
index 69543830aa..a3cfe9fc80 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/Condition.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Condition.java
@@ -25,15 +25,18 @@ import org.springframework.core.type.AnnotationMetadata;
* A single {@code condition} that must be {@linkplain #matches matched} in order
* for a component to be registered.
*
- *
Conditions are checked immediately before a component bean-definition is due to be
- * registered and are free to veto registration based on any criteria that can be
- * determined at that point.
+ *
Conditions are checked immediately before the bean-definition is due to be
+ * registered and are free to veto registration based on any criteria that can
+ * be determined at that point.
*
*
Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
- * and take care to never interact with bean instances.
+ * and take care to never interact with bean instances. For more fine-grained control
+ * of conditions that interact with {@code @Configuration} beans consider the
+ * {@link ConfigurationCondition} interface.
*
* @author Phillip Webb
* @since 4.0
+ * @see ConfigurationCondition
* @see Conditional
* @see ConditionContext
*/
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java b/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java
index 564586501c..541f749c25 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConditionContext.java
@@ -18,6 +18,7 @@ package org.springframework.context.annotation;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
@@ -31,8 +32,8 @@ public interface ConditionContext {
/**
* Returns the {@link BeanDefinitionRegistry} that will hold the bean definition
- * should the condition match.
- * @return the registry (never {@code null})
+ * should the condition match or {@code null} if the registry is not available.
+ * @return the registry or {@code null}
*/
BeanDefinitionRegistry getRegistry();
@@ -45,13 +46,11 @@ public interface ConditionContext {
/**
* Returns the {@link ConfigurableListableBeanFactory} that will hold the bean
- * definition should the condition match. If a
- * {@link ConfigurableListableBeanFactory} is unavailable an
- * {@link IllegalStateException} will be thrown.
- * @return the bean factory
- * @throws IllegalStateException if the bean factory could not be obtained
+ * definition should the condition match or {@code null} if the bean factory is
+ * not available.
+ * @return the bean factory or {@code null}
*/
- ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
+ ConfigurableListableBeanFactory getBeanFactory();
/**
* Returns the {@link ResourceLoader} currently being used or {@code null} if the
@@ -67,4 +66,6 @@ public interface ConditionContext {
*/
ClassLoader getClassLoader();
+ ApplicationContext getApplicationContext();
+
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java b/spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java
new file mode 100644
index 0000000000..32142bc59e
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConditionEvaluator.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.annotation;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.EnvironmentCapable;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.MultiValueMap;
+
+/**
+ * Internal class used to evaluate {@link Conditional} annotations.
+ *
+ * @author Phillip Webb
+ * @since 4.0
+ */
+class ConditionEvaluator {
+
+ private static final String CONDITIONAL_ANNOTATION = Conditional.class.getName();
+
+
+ private final ConditionContextImpl context;
+
+
+ /**
+ * Create a new {@link ConditionEvaluator} instance.
+ */
+ public ConditionEvaluator(BeanDefinitionRegistry registry, Environment environment,
+ ApplicationContext applicationContext, ClassLoader classLoader,
+ ResourceLoader resourceLoader) {
+ this.context = new ConditionContextImpl(registry, environment,
+ applicationContext, classLoader, resourceLoader);
+ }
+
+
+ /**
+ * Determine if an item should be skipped based on {@code @Conditional} annotations.
+ * The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a
+ * {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION})
+ * @param metadata the meta data
+ * @return if the item should be skipped
+ */
+ public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
+ return shouldSkip(metadata, null);
+ }
+
+ /**
+ * Determine if an item should be skipped based on {@code @Conditional} annotations.
+ * @param metadata the meta data
+ * @param phase the phase of the call
+ * @return if the item should be skipped
+ */
+ public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
+ if (metadata == null || !metadata.isAnnotated(CONDITIONAL_ANNOTATION)) {
+ return false;
+ }
+
+ if (phase == null) {
+ if (metadata instanceof AnnotationMetadata &&
+ ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
+ return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
+ }
+ return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
+ }
+
+ for (String[] conditionClasses : getConditionClasses(metadata)) {
+ for (String conditionClass : conditionClasses) {
+ Condition condition = getCondition(conditionClass, context.getClassLoader());
+ ConfigurationPhase requiredPhase = null;
+ if (condition instanceof ConfigurationCondition) {
+ requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
+ }
+ if (requiredPhase == null || requiredPhase == phase) {
+ if (!condition.matches(context, metadata)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private List getConditionClasses(AnnotatedTypeMetadata metadata) {
+ MultiValueMap attributes = metadata.getAllAnnotationAttributes(
+ CONDITIONAL_ANNOTATION, true);
+ Object values = attributes == null ? null : attributes.get("value");
+ return (List) (values == null ? Collections.emptyList() : values);
+ }
+
+ private Condition getCondition(String conditionClassName,
+ ClassLoader classloader) {
+ Class> conditionClass = ClassUtils.resolveClassName(conditionClassName,
+ classloader);
+ return (Condition) BeanUtils.instantiateClass(conditionClass);
+ }
+
+
+ /**
+ * Implementation of a {@link ConditionContext}.
+ */
+ private static class ConditionContextImpl implements ConditionContext {
+
+ private BeanDefinitionRegistry registry;
+
+ private ConfigurableListableBeanFactory beanFactory;
+
+ private Environment environment;
+
+ private ApplicationContext applicationContext;
+
+ private ClassLoader classLoader;
+
+ private ResourceLoader resourceLoader;
+
+
+ public ConditionContextImpl(BeanDefinitionRegistry registry,
+ Environment environment, ApplicationContext applicationContext,
+ ClassLoader classLoader, ResourceLoader resourceLoader) {
+ this.registry = registry;
+ this.beanFactory = deduceBeanFactory(registry);
+ this.environment = environment;
+ this.applicationContext = applicationContext;
+ this.classLoader = classLoader;
+ this.resourceLoader = resourceLoader;
+ }
+
+ private ConfigurableListableBeanFactory deduceBeanFactory(Object source) {
+ if (source == null) {
+ return null;
+ }
+ if (source instanceof ConfigurableListableBeanFactory) {
+ return (ConfigurableListableBeanFactory) source;
+ }
+ else if (source instanceof ConfigurableApplicationContext) {
+ return deduceBeanFactory(((ConfigurableApplicationContext) source).getBeanFactory());
+ }
+ return null;
+ }
+
+ @Override
+ public BeanDefinitionRegistry getRegistry() {
+ if (this.registry != null) {
+ return this.registry;
+ }
+ if(getBeanFactory() != null && getBeanFactory() instanceof BeanDefinitionRegistry) {
+ return (BeanDefinitionRegistry) getBeanFactory();
+ }
+ return null;
+ }
+
+ @Override
+ public Environment getEnvironment() {
+ if (this.environment != null) {
+ return this.environment;
+ }
+ if (getRegistry() != null && getRegistry() instanceof EnvironmentCapable) {
+ return ((EnvironmentCapable) getRegistry()).getEnvironment();
+ }
+ return null;
+ }
+
+ @Override
+ public ConfigurableListableBeanFactory getBeanFactory() {
+ Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
+ return this.beanFactory;
+ }
+
+ @Override
+ public ResourceLoader getResourceLoader() {
+ if (this.resourceLoader != null) {
+ return this.resourceLoader;
+ }
+ if (registry instanceof ResourceLoader) {
+ return (ResourceLoader) registry;
+ }
+ return null;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ if (this.classLoader != null) {
+ return this.classLoader;
+ }
+ if (getResourceLoader() != null) {
+ return getResourceLoader().getClassLoader();
+ }
+ return null;
+ }
+
+ @Override
+ public ApplicationContext getApplicationContext() {
+ if (this.applicationContext != null) {
+ return this.applicationContext;
+ }
+ if (getRegistry() != null && getRegistry() instanceof ApplicationContext) {
+ return (ApplicationContext) getRegistry();
+ }
+ return null;
+ }
+ }
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java
index 9a98714254..898e3ebcd3 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/Conditional.java
@@ -22,11 +22,11 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- * Indicates that a component is is only eligible for registration when all
+ * Indicates that a component is only eligible for registration when all
* {@linkplain #value() specified conditions} match.
*
* A condition is any state that can be determined programmatically
- * immediately before the bean is due to be created (see {@link Condition} for details).
+ * before the bean definition is due to be registered (see {@link Condition} for details).
*
*
The {@code @Conditional} annotation may be used in any of the following ways:
*
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConditionalAnnotationHelper.java b/spring-context/src/main/java/org/springframework/context/annotation/ConditionalAnnotationHelper.java
deleted file mode 100644
index 0eba197672..0000000000
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConditionalAnnotationHelper.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright 2002-2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.annotation;
-
-import java.util.Collections;
-import java.util.List;
-
-import org.springframework.beans.BeanUtils;
-import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.BeanNameGenerator;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.core.env.Environment;
-import org.springframework.core.env.EnvironmentCapable;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.core.type.AnnotatedTypeMetadata;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.MultiValueMap;
-
-/**
- * Helper class used to determine if registration should be skipped based due to a
- * {@code @Conditional} annotation.
- *
- * @author Phillip Webb
- * @since 4.0
- * @see Conditional
- */
-abstract class ConditionalAnnotationHelper {
-
- private static final String CONDITIONAL_ANNOTATION = Conditional.class.getName();
-
-
- public static boolean shouldSkip(BeanDefinition beanDefinition,
- BeanDefinitionRegistry registry, Environment environment,
- BeanNameGenerator beanNameGenerator) {
- if (hasCondition(getMetadata(beanDefinition))) {
- ConditionContextImpl context = new ConditionContextImpl(registry,
- environment, beanNameGenerator);
- return shouldSkip(getMetadata(beanDefinition), context);
- }
- return false;
- }
-
- public static boolean shouldSkip(BeanMethod beanMethod,
- BeanDefinitionRegistry registry, Environment environment,
- BeanNameGenerator beanNameGenerator) {
- if (hasCondition(getMetadata(beanMethod))) {
- ConditionContextImpl context = new ConditionContextImpl(registry,
- environment, beanNameGenerator);
- return shouldSkip(getMetadata(beanMethod), context);
- }
- return false;
- }
-
- public static boolean shouldSkip(ConfigurationClass configurationClass,
- BeanDefinitionRegistry registry, Environment environment,
- BeanNameGenerator beanNameGenerator) {
- if (hasCondition(configurationClass)) {
- ConditionContextImpl context = new ConditionContextImpl(registry,
- environment, beanNameGenerator);
- return shouldSkip(configurationClass, context);
- }
- return false;
- }
-
- public static boolean shouldSkip(ConfigurationClass configClass,
- ConditionContextImpl context) {
- if (configClass == null) {
- return false;
- }
- return shouldSkip(configClass.getMetadata(), context);
- }
-
- private static boolean shouldSkip(AnnotatedTypeMetadata metadata,
- ConditionContextImpl context) {
- if (metadata != null) {
- for (String[] conditionClasses : getConditionClasses(metadata)) {
- for (String conditionClass : conditionClasses) {
- if (!getCondition(conditionClass, context.getClassLoader()).matches(
- context, metadata)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- private static AnnotatedTypeMetadata getMetadata(BeanMethod beanMethod) {
- return (beanMethod == null ? null : beanMethod.getMetadata());
- }
-
- private static AnnotatedTypeMetadata getMetadata(BeanDefinition beanDefinition) {
- if (beanDefinition != null && beanDefinition instanceof AnnotatedBeanDefinition) {
- return ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
- }
- return null;
- }
-
- private static boolean hasCondition(ConfigurationClass configurationClass) {
- if (configurationClass == null) {
- return false;
- }
- return hasCondition(configurationClass.getMetadata())
- || hasCondition(configurationClass.getImportedBy());
- }
-
- private static boolean hasCondition(AnnotatedTypeMetadata metadata) {
- return (metadata != null) && metadata.isAnnotated(CONDITIONAL_ANNOTATION);
- }
-
- @SuppressWarnings("unchecked")
- private static List getConditionClasses(AnnotatedTypeMetadata metadata) {
- MultiValueMap attributes = metadata.getAllAnnotationAttributes(
- CONDITIONAL_ANNOTATION, true);
- Object values = attributes == null ? null : attributes.get("value");
- return (List) (values == null ? Collections.emptyList() : values);
- }
-
- private static Condition getCondition(String conditionClassName,
- ClassLoader classloader) {
- Class> conditionClass = ClassUtils.resolveClassName(conditionClassName,
- classloader);
- return (Condition) BeanUtils.instantiateClass(conditionClass);
- }
-
-
- /**
- * Implementation of a {@link ConditionContext}.
- */
- private static class ConditionContextImpl implements ConditionContext {
-
- private BeanDefinitionRegistry registry;
-
- private ConfigurableListableBeanFactory beanFactory;
-
- private Environment environment;
-
-
- public ConditionContextImpl(BeanDefinitionRegistry registry,
- Environment environment, BeanNameGenerator beanNameGenerator) {
- Assert.notNull(registry, "Registry must not be null");
- this.registry = registry;
- this.beanFactory = deduceBeanFactory(registry);
- this.environment = environment;
- if (this.environment == null) {
- this.environment = deduceEnvironment(registry);
- }
- }
-
-
- private ConfigurableListableBeanFactory deduceBeanFactory(Object source) {
- if (source instanceof ConfigurableListableBeanFactory) {
- return (ConfigurableListableBeanFactory) source;
- }
- else if (source instanceof ConfigurableApplicationContext) {
- return deduceBeanFactory(((ConfigurableApplicationContext) source).getBeanFactory());
- }
- return null;
- }
-
- private Environment deduceEnvironment(BeanDefinitionRegistry registry) {
- if (registry instanceof EnvironmentCapable) {
- return ((EnvironmentCapable) registry).getEnvironment();
- }
- return null;
- }
-
- @Override
- public BeanDefinitionRegistry getRegistry() {
- return this.registry;
- }
-
- @Override
- public Environment getEnvironment() {
- return this.environment;
- }
-
- @Override
- public ConfigurableListableBeanFactory getBeanFactory() {
- Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
- return this.beanFactory;
- }
-
- @Override
- public ResourceLoader getResourceLoader() {
- if (registry instanceof ResourceLoader) {
- return (ResourceLoader) registry;
- }
- return null;
- }
-
- @Override
- public ClassLoader getClassLoader() {
- ResourceLoader resourceLoader = getResourceLoader();
- return (resourceLoader == null ? null : resourceLoader.getClassLoader());
- }
- }
-
-}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
index 2e91b45d5f..9138e2357f 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java
@@ -16,6 +16,7 @@
package org.springframework.context.annotation;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -61,6 +62,9 @@ final class ConfigurationClass {
private final Map> importedResources =
new LinkedHashMap>();
+ private final Set importBeanDefinitionRegistrars =
+ new LinkedHashSet();
+
/**
* Create a new {@link ConfigurationClass} with the given name.
@@ -173,11 +177,18 @@ final class ConfigurationClass {
this.importedResources.put(importedResource, readerClass);
}
+ public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar) {
+ this.importBeanDefinitionRegistrars.add(registrar);
+ }
+
+ public Set getImportBeanDefinitionRegistrars() {
+ return Collections.unmodifiableSet(importBeanDefinitionRegistrars);
+ }
+
public Map> getImportedResources() {
return this.importedResources;
}
-
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {
@@ -208,7 +219,6 @@ final class ConfigurationClass {
}
}
-
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof ConfigurationClass &&
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
index 27f417f655..4aa586dd2c 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java
@@ -16,8 +16,6 @@
package org.springframework.context.annotation;
-import static org.springframework.context.annotation.MetadataUtils.attributesFor;
-
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,6 +26,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire;
@@ -43,6 +42,8 @@ import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
@@ -52,6 +53,8 @@ import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.StringUtils;
+import static org.springframework.context.annotation.MetadataUtils.*;
+
/**
* Reads a given fully-populated set of ConfigurationClass instances, registering bean
* definitions with the given {@link BeanDefinitionRegistry} based on its contents.
@@ -84,15 +87,17 @@ class ConfigurationClassBeanDefinitionReader {
private final BeanNameGenerator importBeanNameGenerator;
+ private final ConditionEvaluator conditionEvaluator;
/**
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
* to populate the given {@link BeanDefinitionRegistry}.
*/
public ConfigurationClassBeanDefinitionReader(
- BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
- ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
- ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator) {
+ BeanDefinitionRegistry registry, ApplicationContext applicationContext,
+ SourceExtractor sourceExtractor, ProblemReporter problemReporter,
+ MetadataReaderFactory metadataReaderFactory, ResourceLoader resourceLoader,
+ Environment environment, BeanNameGenerator importBeanNameGenerator) {
this.registry = registry;
this.sourceExtractor = sourceExtractor;
@@ -101,6 +106,8 @@ class ConfigurationClassBeanDefinitionReader {
this.resourceLoader = resourceLoader;
this.environment = environment;
this.importBeanNameGenerator = importBeanNameGenerator;
+ this.conditionEvaluator = new ConditionEvaluator(registry, environment,
+ applicationContext, null, resourceLoader);
}
@@ -109,8 +116,9 @@ class ConfigurationClassBeanDefinitionReader {
* based on its contents.
*/
public void loadBeanDefinitions(Set configurationModel) {
+ TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
- loadBeanDefinitionsForConfigurationClass(configClass);
+ loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
@@ -118,7 +126,13 @@ class ConfigurationClassBeanDefinitionReader {
* Read a particular {@link ConfigurationClass}, registering bean definitions for the
* class itself, all its {@link Bean} methods
*/
- public void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
+ private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
+ TrackedConditionEvaluator trackedConditionEvaluator) {
+ if (trackedConditionEvaluator.shouldSkip(configClass)) {
+ removeBeanDefinition(configClass);
+ return;
+ }
+
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
@@ -126,6 +140,17 @@ class ConfigurationClassBeanDefinitionReader {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
+ loadBeanDefinitionsFromRegistrars(configClass.getMetadata(), configClass.getImportBeanDefinitionRegistrars());
+ }
+
+ private void removeBeanDefinition(ConfigurationClass configClass) {
+ if (StringUtils.hasLength(configClass.getBeanName())) {
+ try {
+ this.registry.removeBeanDefinition(configClass.getBeanName());
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ }
+ }
}
/**
@@ -153,8 +178,8 @@ class ConfigurationClassBeanDefinitionReader {
* with the BeanDefinitionRegistry based on its contents.
*/
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
- if (ConditionalAnnotationHelper.shouldSkip(beanMethod, this.registry,
- this.environment, this.importBeanNameGenerator)) {
+ if (conditionEvaluator.shouldSkip(beanMethod.getMetadata(),
+ ConfigurationPhase.REGISTER_BEAN)) {
return;
}
ConfigurationClass configClass = beanMethod.getConfigurationClass();
@@ -300,6 +325,14 @@ class ConfigurationClassBeanDefinitionReader {
}
}
+ private void loadBeanDefinitionsFromRegistrars(
+ AnnotationMetadata importingClassMetadata,
+ Set importBeanDefinitionRegistrars) {
+ for (ImportBeanDefinitionRegistrar registrar : importBeanDefinitionRegistrars) {
+ registrar.registerBeanDefinitions(importingClassMetadata, this.registry);
+ }
+ }
+
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
@@ -357,4 +390,32 @@ class ConfigurationClassBeanDefinitionReader {
}
}
+
+ /**
+ * Evaluate {@Code @Conditional} annotations, tracking results and taking into
+ * account 'imported by'.
+ */
+ private class TrackedConditionEvaluator {
+
+ private final Map skipped = new HashMap();
+
+ public boolean shouldSkip(ConfigurationClass configClass) {
+ Boolean skip = this.skipped.get(configClass);
+ if (skip == null) {
+ if (configClass.isImported()) {
+ if (shouldSkip(configClass.getImportedBy())) {
+ // The config that imported this one was skipped, therefore we are skipped
+ skip = true;
+ }
+ }
+ if (skip == null) {
+ skip = conditionEvaluator.shouldSkip(configClass.getMetadata(),
+ ConfigurationPhase.REGISTER_BEAN);
+ }
+ this.skipped.put(configClass, skip);
+ }
+ return skip;
+ }
+ }
+
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java
index 967f8f231e..d2210c812f 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java
@@ -411,7 +411,8 @@ class ConfigurationClassEnhancer {
Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated bean factory field");
Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
- Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory);
+ Assert.state(beanFactory != null, "The BeanFactory has not been injected into the @Configuration class");
+ Assert.state(beanFactory instanceof ConfigurableBeanFactory, "The injected BeanFactory is not a ConfigurableBeanFactory");
return (ConfigurableBeanFactory) beanFactory;
}
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
index 294d94bb04..ba0052358a 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@@ -17,8 +17,7 @@
package org.springframework.context.annotation;
import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -47,7 +46,10 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
+import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.core.NestedIOException;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
@@ -62,7 +64,6 @@ import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AssignableTypeFilter;
-import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
@@ -75,7 +76,8 @@ import static org.springframework.context.annotation.MetadataUtils.*;
*
* This class helps separate the concern of parsing the structure of a Configuration
* class from the concern of registering BeanDefinition objects based on the
- * content of that model.
+ * content of that model (with the exception of {@code @ComponentScan} annotations which
+ * need to be registered immediately).
*
*
This ASM-based implementation avoids reflection and eager class loading in order to
* interoperate effectively with lazy class loading in a Spring ApplicationContext.
@@ -107,8 +109,6 @@ class ConfigurationClassParser {
private final BeanDefinitionRegistry registry;
- private final BeanNameGenerator beanNameGenerator;
-
private final ComponentScanAnnotationParser componentScanParser;
private final Set configurationClasses = new LinkedHashSet();
@@ -121,6 +121,8 @@ class ConfigurationClassParser {
private final List deferredImportSelectors = new LinkedList();
+ private final ConditionEvaluator conditionEvaluator;
+
/**
* Create a new {@link ConfigurationClassParser} instance that will be used
@@ -128,16 +130,18 @@ class ConfigurationClassParser {
*/
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
- BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
+ BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry,
+ ApplicationContext applicationContext) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
- this.beanNameGenerator = componentScanBeanNameGenerator;
this.componentScanParser = new ComponentScanAnnotationParser(
resourceLoader, environment, componentScanBeanNameGenerator, registry);
+ this.conditionEvaluator = new ConditionEvaluator(registry, environment,
+ applicationContext, null, resourceLoader);
}
@@ -165,7 +169,7 @@ class ConfigurationClassParser {
* @param beanName may be null, but if populated represents the bean id
* (assumes that this configuration class was configured via XML)
*/
- public void parse(String className, String beanName) throws IOException {
+ protected final void parse(String className, String beanName) throws IOException {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
@@ -175,13 +179,14 @@ class ConfigurationClassParser {
* @param clazz the Class to parse
* @param beanName must not be null (as of Spring 3.1.1)
*/
- public void parse(Class> clazz, String beanName) throws IOException {
+ protected final void parse(Class> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
- if (ConditionalAnnotationHelper.shouldSkip(configClass, this.registry, this.environment, this.beanNameGenerator)) {
+
+ if (conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
@@ -197,56 +202,58 @@ class ConfigurationClassParser {
}
// Recursively process the configuration class and its superclass hierarchy.
- AnnotationMetadata metadata = configClass.getMetadata();
+ SourceClass sourceClass = asSourceClass(configClass);
do {
- metadata = doProcessConfigurationClass(configClass, metadata);
+ sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
- while (metadata != null);
+ while (sourceClass != null);
this.configurationClasses.add(configClass);
}
/**
- * @return annotation metadata of superclass, {@code null} if none found or previously processed
+ * Apply processing and build a complete {@link ConfigurationClass} by reading the
+ * annotations, members and methods from the source class. This method can be called
+ * multiple times as relevant sources are discovered.
+ * @param configClass the configuration class being build
+ * @param sourceClass a source class
+ * @return the superclass, {@code null} if none found or previously processed
*/
- protected AnnotationMetadata doProcessConfigurationClass(
- ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
+ protected final SourceClass doProcessConfigurationClass(
+ ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// recursively process any member (nested) classes first
- processMemberClasses(configClass, metadata);
+ processMemberClasses(configClass, sourceClass);
// process any @PropertySource annotations
- AnnotationAttributes propertySource = attributesFor(metadata, org.springframework.context.annotation.PropertySource.class);
+ AnnotationAttributes propertySource = attributesFor(sourceClass.getMetadata(), org.springframework.context.annotation.PropertySource.class);
if (propertySource != null) {
processPropertySource(propertySource);
}
// process any @ComponentScan annotations
- AnnotationAttributes componentScan = attributesFor(metadata, ComponentScan.class);
+ AnnotationAttributes componentScan = attributesFor(sourceClass.getMetadata(), ComponentScan.class);
if (componentScan != null) {
// the config class is annotated with @ComponentScan -> perform the scan immediately
- Set scannedBeanDefinitions =
- this.componentScanParser.parse(componentScan, metadata.getClassName());
+ if (!conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
+ Set scannedBeanDefinitions =
+ this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
- // check the set of scanned definitions for any further config classes and parse recursively if necessary
- for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
- if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
- this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
+ // check the set of scanned definitions for any further config classes and parse recursively if necessary
+ for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
+ if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
+ parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
+ }
}
}
}
// process any @Import annotations
- Set imports = new LinkedHashSet();
- Set visited = new LinkedHashSet();
- collectImports(metadata, imports, visited);
- if (!imports.isEmpty()) {
- processImport(configClass, imports, true);
- }
+ processImports(configClass, getImports(sourceClass), true);
// process any @ImportResource annotations
- if (metadata.isAnnotated(ImportResource.class.getName())) {
- AnnotationAttributes importResource = attributesFor(metadata, ImportResource.class);
+ if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
+ AnnotationAttributes importResource = attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("value");
Class extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
@@ -255,34 +262,22 @@ class ConfigurationClassParser {
}
// process individual @Bean methods
- Set beanMethods = metadata.getAnnotatedMethods(Bean.class.getName());
+ Set beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// process superclass, if any
- if (metadata.hasSuperClass()) {
- String superclass = metadata.getSuperClassName();
+ if (sourceClass.getMetadata().hasSuperClass()) {
+ String superclass = sourceClass.getMetadata().getSuperClassName();
if (!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// superclass found, return its annotation metadata and recurse
- if (metadata instanceof StandardAnnotationMetadata) {
- Class> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
- return new StandardAnnotationMetadata(clazz.getSuperclass(), true);
+ try {
+ return sourceClass.getSuperClass();
}
- else if (superclass.startsWith("java")) {
- // never load core JDK classes via ASM, in particular not java.lang.Object!
- try {
- return new StandardAnnotationMetadata(
- this.resourceLoader.getClassLoader().loadClass(superclass), true);
- }
- catch (ClassNotFoundException ex) {
- throw new IllegalStateException(ex);
- }
- }
- else {
- MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass);
- return reader.getAnnotationMetadata();
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException(ex);
}
}
}
@@ -293,24 +288,13 @@ class ConfigurationClassParser {
/**
* Register member (nested) classes that happen to be configuration classes themselves.
- * @param metadata the metadata representation of the containing class
+ * @param sourceClass the source class to process
* @throws IOException if there is any problem reading metadata from a member class
*/
- private void processMemberClasses(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
- if (metadata instanceof StandardAnnotationMetadata) {
- for (Class> memberClass : ((StandardAnnotationMetadata) metadata).getIntrospectedClass().getDeclaredClasses()) {
- if (ConfigurationClassUtils.isConfigurationCandidate(new StandardAnnotationMetadata(memberClass))) {
- processConfigurationClass(new ConfigurationClass(memberClass, configClass));
- }
- }
- }
- else {
- for (String memberClassName : metadata.getMemberClassNames()) {
- MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName);
- AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata();
- if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) {
- processConfigurationClass(new ConfigurationClass(reader, configClass));
- }
+ private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
+ for (SourceClass memberClass : sourceClass.getMemberClasses()) {
+ if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata())) {
+ processConfigurationClass(memberClass.asConfigClass(configClass));
}
}
}
@@ -350,59 +334,46 @@ class ConfigurationClassParser {
}
}
+ /**
+ * Returns {@code @Import} class, considering all meta-annotations.
+ */
+ private Set getImports(SourceClass sourceClass) throws IOException {
+ Set imports = new LinkedHashSet();
+ Set visited = new LinkedHashSet();
+ collectImports(sourceClass, imports, visited);
+ return imports;
+ }
+
/**
* Recursively collect all declared {@code @Import} values. Unlike most
* meta-annotations it is valid to have several {@code @Import}s declared with
* different values, the usual process or returning values from the first
* meta-annotation on a class is not sufficient.
- * For example, it is common for a {@code @Configuration} class to declare direct
+ *
+ * For example, it is common for a {@code @Configuration} class to declare direct
* {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
* annotation.
- * @param metadata the metadata representation of the class to search
+ *
+ * @param sourceClass the class to search
* @param imports the imports collected so far
* @param visited used to track visited classes to prevent infinite recursion
* @throws IOException if there is any problem reading metadata from the named class
*/
- private void collectImports(AnnotationMetadata metadata, Set imports, Set visited) throws IOException {
- String className = metadata.getClassName();
- if (visited.add(className)) {
- if (metadata instanceof StandardAnnotationMetadata) {
- StandardAnnotationMetadata stdMetadata = (StandardAnnotationMetadata) metadata;
- for (Annotation ann : stdMetadata.getIntrospectedClass().getAnnotations()) {
- if (!ann.annotationType().getName().startsWith("java") && !(ann instanceof Import)) {
- collectImports(new StandardAnnotationMetadata(ann.annotationType()), imports, visited);
- }
- }
- Map attributes = stdMetadata.getAnnotationAttributes(Import.class.getName(), false);
- if (attributes != null) {
- Class[] value = (Class[]) attributes.get("value");
- if (!ObjectUtils.isEmpty(value)) {
- imports.addAll(Arrays.asList(value));
- }
- }
- }
- else {
- for (String annotationType : metadata.getAnnotationTypes()) {
- if (!className.startsWith("java") && !className.equals(Import.class.getName())) {
- try {
- collectImports(
- new StandardAnnotationMetadata(this.resourceLoader.getClassLoader().loadClass(annotationType)),
- imports, visited);
- }
- catch (ClassNotFoundException ex) {
- //
- }
- }
- }
- Map attributes = metadata.getAnnotationAttributes(Import.class.getName(), true);
- if (attributes != null) {
- String[] value = (String[]) attributes.get("value");
- if (!ObjectUtils.isEmpty(value)) {
- imports.addAll(Arrays.asList(value));
+ private void collectImports(SourceClass sourceClass, Set imports,
+ Set visited) throws IOException {
+ try {
+ if (visited.add(sourceClass)) {
+ for (SourceClass annotation : sourceClass.getAnnotations()) {
+ if(!annotation.getMetadata().getClassName().startsWith("java") && !annotation.isAssignable(Import.class)) {
+ collectImports(annotation, imports, visited);
}
}
+ imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
+ catch (ClassNotFoundException ex) {
+ throw new NestedIOException("Unable to collect imports", ex);
+ }
}
private void processDeferredImportSelectors() {
@@ -411,16 +382,21 @@ class ConfigurationClassParser {
try {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
- processImport(configClass, Arrays.asList(imports), false);
+ processImports(configClass, asSourceClasses(imports), false);
}
- catch (IOException ex) {
+ catch (Exception ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: ", ex);
}
}
this.deferredImportSelectors.clear();
}
- private void processImport(ConfigurationClass configClass, Collection> classesToImport, boolean checkForCircularImports) throws IOException {
+ private void processImports(ConfigurationClass configClass,
+ Collection sourceClasses, boolean checkForCircularImports)
+ throws IOException {
+ if(sourceClasses.isEmpty()) {
+ return;
+ }
if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
}
@@ -428,37 +404,33 @@ class ConfigurationClassParser {
this.importStack.push(configClass);
AnnotationMetadata importingClassMetadata = configClass.getMetadata();
try {
- for (Object candidate : classesToImport) {
- Object candidateToCheck = (candidate instanceof Class ? (Class) candidate :
- this.metadataReaderFactory.getMetadataReader((String) candidate));
- if (checkAssignability(ImportSelector.class, candidateToCheck)) {
+ for (SourceClass candidate : sourceClasses) {
+ if (candidate.isAssignable(ImportSelector.class)) {
// the candidate class is an ImportSelector -> delegate to it to determine imports
- Class> candidateClass = (candidate instanceof Class ? (Class) candidate :
- this.resourceLoader.getClassLoader().loadClass((String) candidate));
+ Class> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
invokeAwareMethods(selector);
if(selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
- processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false);
+ String[] importClassNames = selector.selectImports(importingClassMetadata);
+ Collection importSourceClasses = asSourceClasses(importClassNames);
+ processImports(configClass, importSourceClasses, false);
}
}
- else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
+ else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
- Class> candidateClass = (candidate instanceof Class ? (Class) candidate :
- this.resourceLoader.getClassLoader().loadClass((String) candidate));
+ Class> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
- registrar.registerBeanDefinitions(importingClassMetadata, this.registry);
+ configClass.addImportBeanDefinitionRegistrar(registrar);
}
else {
// candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
this.importStack.registerImport(importingClassMetadata.getClassName(),
- (candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
- processConfigurationClass((candidateToCheck instanceof Class ?
- new ConfigurationClass((Class) candidateToCheck, configClass) :
- new ConfigurationClass((MetadataReader) candidateToCheck, configClass)));
+ candidate.getMetadata().getClassName());
+ processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
@@ -471,21 +443,15 @@ class ConfigurationClassParser {
}
}
- private boolean checkAssignability(Class> clazz, Object candidate) throws IOException {
- if (candidate instanceof Class) {
- return clazz.isAssignableFrom((Class) candidate);
- }
- else {
- return new AssignableTypeFilter(clazz).match((MetadataReader) candidate, this.metadataReaderFactory);
- }
- }
-
/**
* Invoke {@link ResourceLoaderAware}, {@link BeanClassLoaderAware} and
* {@link BeanFactoryAware} contracts if implemented by the given {@code bean}.
*/
private void invokeAwareMethods(Object importStrategyBean) {
if (importStrategyBean instanceof Aware) {
+ if (importStrategyBean instanceof EnvironmentAware) {
+ ((EnvironmentAware) importStrategyBean).setEnvironment(this.environment);
+ }
if (importStrategyBean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) importStrategyBean).setResourceLoader(this.resourceLoader);
}
@@ -524,10 +490,68 @@ class ConfigurationClassParser {
return this.importStack;
}
+ /**
+ * Factory method to obtain a {@link SourceClass} from a {@link ConfigurationClass}.
+ */
+ public SourceClass asSourceClass(ConfigurationClass configurationClass)
+ throws IOException {
+ try {
+ AnnotationMetadata metadata = configurationClass.getMetadata();
+ if (metadata instanceof StandardAnnotationMetadata) {
+ return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
+ }
+ return asSourceClass(configurationClass.getMetadata().getClassName());
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ /**
+ * Factory method to obtain a {@link SourceClass} from a {@link Class}.
+ */
+ public SourceClass asSourceClass(Class> classType)
+ throws ClassNotFoundException, IOException {
+ try {
+ // Sanity test that we can read annotations, if not fall back to ASM
+ classType.getAnnotations();
+ }
+ catch (Throwable ex) {
+ return asSourceClass(classType.getName());
+ }
+ return new SourceClass(classType);
+ }
+
+ /**
+ * Factory method to obtain {@link SourceClass}s from class names.
+ */
+ public Collection asSourceClasses(String[] classNamess)
+ throws ClassNotFoundException, IOException {
+ List annotatedClasses = new ArrayList();
+ for (String className : classNamess) {
+ annotatedClasses.add(asSourceClass(className));
+ }
+ return annotatedClasses;
+ }
+
+ /**
+ * Factory method to obtain a {@link SourceClass} from a class name.
+ */
+ public SourceClass asSourceClass(String className)
+ throws ClassNotFoundException, IOException {
+ if (className.startsWith("java")) {
+ // Never use ASM for core java types
+ return new SourceClass(this.resourceLoader.getClassLoader().loadClass(
+ className));
+ }
+ return new SourceClass(this.metadataReaderFactory.getMetadataReader(className));
+ }
+
interface ImportRegistry {
String getImportingClassFor(String importedClass);
+
}
@@ -607,6 +631,151 @@ class ConfigurationClassParser {
}
+ /**
+ * Simple wrapper that allows annotated source classes to be dealt with in a uniform
+ * manor, regardless of how they are loaded.
+ */
+ private class SourceClass {
+
+ private final Object source; // Class or MetaDataReader
+
+ private final AnnotationMetadata metadata;
+
+
+ private SourceClass(Object source) {
+ this.source = source;
+ if (source instanceof Class>) {
+ this.metadata = new StandardAnnotationMetadata((Class>) source, true);
+ }
+ else {
+ this.metadata = ((MetadataReader) source).getAnnotationMetadata();
+ }
+ }
+
+
+ public Class> loadClass() throws ClassNotFoundException {
+ if(source instanceof Class>) {
+ return (Class>) source;
+ }
+ String className = ((MetadataReader) source).getClassMetadata().getClassName();
+ return resourceLoader.getClassLoader().loadClass(className);
+ }
+
+ public boolean isAssignable(Class> clazz) throws IOException {
+ if (source instanceof Class) {
+ return clazz.isAssignableFrom((Class) source);
+ }
+ return new AssignableTypeFilter(clazz).match((MetadataReader) source,
+ metadataReaderFactory);
+ }
+
+ public ConfigurationClass asConfigClass(ConfigurationClass importedBy)
+ throws IOException {
+ if (this.source instanceof Class>) {
+ return new ConfigurationClass((Class>) this.source, importedBy);
+ }
+ return new ConfigurationClass((MetadataReader) source, importedBy);
+ }
+
+ public Collection getMemberClasses() throws IOException {
+ List members = new ArrayList();
+ if (source instanceof Class>) {
+ Class> sourceClass = (Class>) source;
+ for (Class> declaredClass : sourceClass.getDeclaredClasses()) {
+ try {
+ members.add(asSourceClass(declaredClass));
+ }
+ catch (ClassNotFoundException e) {
+ }
+ }
+ }
+ else {
+ MetadataReader sourceReader = (MetadataReader) source;
+ for (String memberClassName : sourceReader.getClassMetadata().getMemberClassNames()) {
+ try {
+ members.add(asSourceClass(memberClassName));
+ }
+ catch (ClassNotFoundException e) {
+ }
+ }
+ }
+ return members;
+ }
+
+ public SourceClass getSuperClass() throws ClassNotFoundException, IOException {
+ if (source instanceof Class>) {
+ return asSourceClass(((Class>) source).getSuperclass());
+ }
+ return asSourceClass(((MetadataReader) source).getClassMetadata().getSuperClassName());
+ }
+
+ public Set getAnnotations() throws ClassNotFoundException, IOException {
+ Set annotations = new LinkedHashSet();
+ for(String annotation : getMetadata().getAnnotationTypes()) {
+ annotations.add(getRelated(annotation));
+ }
+ return annotations;
+ }
+
+ public Collection getAnnotationAttributes(String annotationType,
+ String attribute) throws ClassNotFoundException, IOException {
+ Map annotationAttributes = getMetadata().getAnnotationAttributes(
+ annotationType, true);
+ if (annotationAttributes == null
+ || !annotationAttributes.containsKey(attribute)) {
+ return Collections.emptySet();
+ }
+ String[] classNames = (String[]) annotationAttributes.get(attribute);
+ Set rtn = new LinkedHashSet();
+ for (String className : classNames) {
+ rtn.add(getRelated(className));
+ }
+ return rtn;
+ }
+
+ private SourceClass getRelated(String className) throws IOException,
+ ClassNotFoundException {
+ if (source instanceof Class>) {
+ try {
+ Class> clazz = resourceLoader.getClassLoader().loadClass(className);
+ return asSourceClass(clazz);
+ }
+ catch (ClassNotFoundException ex) {
+ }
+ }
+ return asSourceClass(className);
+ }
+
+ public AnnotationMetadata getMetadata() {
+ return this.metadata;
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (obj instanceof SourceClass) {
+ return toString().equals(obj.toString());
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return getMetadata().getClassName();
+ }
+ }
+
+
/**
* {@link Problem} registered upon detection of a circular {@link Import}.
*/
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
index 944fd051f2..f659878fb0 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java
@@ -16,6 +16,7 @@
package org.springframework.context.annotation;
+import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -26,17 +27,19 @@ import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
@@ -47,8 +50,11 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
+import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
import org.springframework.context.annotation.ConfigurationClassParser.ImportRegistry;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
@@ -86,7 +92,8 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*;
* @since 3.0
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
- ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
+ ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware, ApplicationContextAware,
+ Ordered {
private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor";
@@ -94,6 +101,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private static final String IMPORT_REGISTRY_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
+ private static final String ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME =
+ ConfigurationClassPostProcessor.class.getName() + ".enhancedConfigurationProcessor";
+
private final Log logger = LogFactory.getLog(getClass());
@@ -103,6 +113,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private Environment environment;
+ private ApplicationContext applicationContext;
+
private ResourceLoader resourceLoader = new DefaultResourceLoader();
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
@@ -131,6 +143,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
};
+
/**
* Set the {@link SourceExtractor} to use for generated bean definitions
* that correspond to {@link Bean} factory methods.
@@ -190,6 +203,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
this.environment = environment;
}
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
@@ -214,6 +233,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
+ RootBeanDefinition ecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);
+ ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME, ecbpp);
+
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
@@ -280,7 +303,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
- this.resourceLoader, this.componentScanBeanNameGenerator, registry);
+ this.resourceLoader, this.componentScanBeanNameGenerator, registry,
+ this.applicationContext);
parser.parse(configCandidates);
parser.validate();
@@ -302,15 +326,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
- registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
+ registry, this.applicationContext, this.sourceExtractor,
+ this.problemReporter, this.metadataReaderFactory,
this.resourceLoader, this.environment, this.importBeanNameGenerator);
}
- for (ConfigurationClass configurationClass : parser.getConfigurationClasses()) {
- if (!ConditionalAnnotationHelper.shouldSkip(configurationClass, registry,
- this.environment, this.importBeanNameGenerator)) {
- reader.loadBeanDefinitionsForConfigurationClass(configurationClass);
- }
- }
+
+ reader.loadBeanDefinitions(parser.getConfigurationClasses());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {
@@ -366,6 +387,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
}
+ @Override
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
+ }
+
private static class ImportAwareBeanPostProcessor implements PriorityOrdered, BeanFactoryAware, BeanPostProcessor {
@@ -410,4 +436,40 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
}
+
+ /**
+ * {@link InstantiationAwareBeanPostProcessorAdapter} that ensures
+ * {@link EnhancedConfiguration} beans are injected with the {@link BeanFactory}
+ * before the {@link AutowiredAnnotationBeanPostProcessor} runs (SPR-10668).
+ */
+ private static class EnhancedConfigurationBeanPostProcessor extends
+ InstantiationAwareBeanPostProcessorAdapter implements PriorityOrdered,
+ BeanFactoryAware {
+
+ private BeanFactory beanFactory;
+
+ @Override
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
+ }
+
+ @Override
+ public PropertyValues postProcessPropertyValues(PropertyValues pvs,
+ PropertyDescriptor[] pds, Object bean, String beanName)
+ throws BeansException {
+ // Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
+ // postProcessPropertyValues method attempts to auto-wire other configuration
+ // beans.
+ if (bean instanceof EnhancedConfiguration) {
+ ((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
+ }
+ return pvs;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
+ }
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassUtils.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassUtils.java
index d7bed91502..46a58b7eb0 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassUtils.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,6 @@ import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
-import org.springframework.stereotype.Component;
/**
* Utilities for processing @{@link Configuration} classes.
@@ -105,7 +104,6 @@ abstract class ConfigurationClassUtils {
return false; // do not consider an interface or an annotation
}
return metadata.isAnnotated(Import.class.getName()) ||
- metadata.isAnnotated(Component.class.getName()) ||
metadata.hasAnnotatedMethods(Bean.class.getName());
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationCondition.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationCondition.java
new file mode 100644
index 0000000000..641c50b62d
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationCondition.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.annotation;
+
+/**
+ * A {@link Condition} that offers more fine-grained control when used with
+ * {@code @Configuration}. Allows certain {@link Condition}s to adapt when they match
+ * based on the configuration phase. For example, a condition that checks if a bean has
+ * already been registered might choose to only be evaluated during the
+ * {@link ConfigurationPhase#REGISTER_BEAN REGISTER_BEAN} {@link ConfigurationPhase}.
+ *
+ * @author Phillip Webb
+ * @since 4.0
+ */
+public interface ConfigurationCondition extends Condition {
+
+ /**
+ * Returns the {@link ConfigurationPhase} in which the condition should be evaluated.
+ */
+ ConfigurationPhase getConfigurationPhase();
+
+ /**
+ * The various configuration phases where the condition could be evaluated.
+ */
+ public static enum ConfigurationPhase {
+
+ /**
+ * The {@link Condition} should be evaluated as a {@code @Configuration} class is
+ * being parsed.
+ *
+ * If the condition does not match at this point the {@code @Configuration}
+ * class will not be added.
+ */
+ PARSE_CONFIGURATION,
+
+ /**
+ * The {@link Condition} should be evaluated when adding a regular (non
+ * {@code @Configuration}) bean. The condition will not prevent
+ * {@code @Configuration} classes from being added.
+ *
+ *
At the time that the condition is evaluated all {@code @Configuration}s
+ * will have been parsed.
+ */
+ REGISTER_BEAN
+ }
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrar.java b/spring-context/src/main/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrar.java
index 648a8e9211..6795d6172c 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrar.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrar.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ import org.springframework.core.type.AnnotationMetadata;
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #registerBeanDefinitions}:
*
+ * {@link org.springframework.context.EnvironmentAware}
* {@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* {@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* {@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ImportSelector.java b/spring-context/src/main/java/org/springframework/context/annotation/ImportSelector.java
index 5d8848001d..b98719f97f 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/ImportSelector.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/ImportSelector.java
@@ -27,6 +27,7 @@ import org.springframework.core.type.AnnotationMetadata;
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #selectImports}:
*
+ * {@link org.springframework.context.EnvironmentAware}
* {@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* {@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* {@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
diff --git a/spring-context/src/main/java/org/springframework/remoting/rmi/RmiRegistryFactoryBean.java b/spring-context/src/main/java/org/springframework/remoting/rmi/RmiRegistryFactoryBean.java
index 0a6ded9114..b7107934b3 100644
--- a/spring-context/src/main/java/org/springframework/remoting/rmi/RmiRegistryFactoryBean.java
+++ b/spring-context/src/main/java/org/springframework/remoting/rmi/RmiRegistryFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -181,7 +181,7 @@ public class RmiRegistryFactoryBean implements FactoryBean, Initializi
throws RemoteException {
if (registryHost != null) {
- // Host explictly specified: only lookup possible.
+ // Host explicitly specified: only lookup possible.
if (logger.isInfoEnabled()) {
logger.info("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
}
diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationPostProcessor.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationPostProcessor.java
index 7a61e6b2b1..f016ca1d9a 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationPostProcessor.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationPostProcessor.java
@@ -83,7 +83,12 @@ public class MethodValidationPostProcessor extends AbstractAdvisingBeanPostProce
* Default is the default ValidatorFactory's default Validator.
*/
public void setValidator(Validator validator) {
- this.validator = validator;
+ if(validator instanceof LocalValidatorFactoryBean) {
+ this.validator = ((LocalValidatorFactoryBean) validator).getValidator();
+ }
+ else {
+ this.validator = validator;
+ }
}
/**
diff --git a/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java b/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java
index 41d717d692..19f9ded42a 100644
--- a/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java
+++ b/spring-context/src/test/java/org/springframework/cache/config/AbstractAnnotationTests.java
@@ -367,6 +367,16 @@ public abstract class AbstractAnnotationTests {
assertSame(r2, secondary.get(o).get());
}
+ public void testPutRefersToResult(CacheableService> service) throws Exception {
+ Long id = Long.MIN_VALUE;
+ TestEntity entity = new TestEntity();
+ Cache primary = cm.getCache("primary");
+ assertNull(primary.get(id));
+ assertNull(entity.getId());
+ service.putRefersToResult(entity);
+ assertSame(entity, primary.get(id).get());
+ }
+
public void testMultiCacheAndEvict(CacheableService> service) {
String methodName = "multiCacheAndEvict";
@@ -621,6 +631,16 @@ public abstract class AbstractAnnotationTests {
testMultiPut(ccs);
}
+ @Test
+ public void testPutRefersToResult() throws Exception {
+ testPutRefersToResult(cs);
+ }
+
+ @Test
+ public void testClassPutRefersToResult() throws Exception {
+ testPutRefersToResult(ccs);
+ }
+
@Test
public void testMultiCacheAndEvict() {
testMultiCacheAndEvict(cs);
diff --git a/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java
index f168ab990a..0c5ca6712d 100644
--- a/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java
+++ b/spring-context/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java
@@ -31,6 +31,7 @@ import org.springframework.cache.annotation.Caching;
public class AnnotatedClassCacheableService implements CacheableService {
private final AtomicLong counter = new AtomicLong();
+
public static final AtomicLong nullInvocations = new AtomicLong();
@Override
@@ -164,4 +165,11 @@ public class AnnotatedClassCacheableService implements CacheableService
public Object multiUpdate(Object arg1) {
return arg1;
}
+
+ @Override
+ @CachePut(value="primary", key="#result.id")
+ public TestEntity putRefersToResult(TestEntity arg1) {
+ arg1.setId(Long.MIN_VALUE);
+ return arg1;
+ }
}
diff --git a/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java b/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java
index 98c1da1e03..6e33a8a471 100644
--- a/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java
+++ b/spring-context/src/test/java/org/springframework/cache/config/CacheableService.java
@@ -70,4 +70,6 @@ public interface CacheableService {
T multiConditionalCacheAndEvict(Object arg1);
T multiUpdate(Object arg1);
+
+ TestEntity putRefersToResult(TestEntity arg1);
}
diff --git a/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java
index f987d7bbe6..c7b1df4fcf 100644
--- a/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java
+++ b/spring-context/src/test/java/org/springframework/cache/config/DefaultCacheableService.java
@@ -170,4 +170,11 @@ public class DefaultCacheableService implements CacheableService {
public Long multiUpdate(Object arg1) {
return Long.valueOf(arg1.toString());
}
+
+ @Override
+ @CachePut(value="primary", key="#result.id")
+ public TestEntity putRefersToResult(TestEntity arg1) {
+ arg1.setId(Long.MIN_VALUE);
+ return arg1;
+ }
}
diff --git a/spring-context/src/test/java/org/springframework/cache/config/TestEntity.java b/spring-context/src/test/java/org/springframework/cache/config/TestEntity.java
new file mode 100644
index 0000000000..4c43007158
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/cache/config/TestEntity.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.cache.config;
+
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Simple test entity for use with caching tests.
+ *
+ * @author Michael Plšd
+ */
+public class TestEntity {
+
+ private Long id;
+
+ public Long getId() {
+ return this.id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @Override
+ public int hashCode() {
+ return ObjectUtils.nullSafeHashCode(this.id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (obj instanceof TestEntity) {
+ return ObjectUtils.nullSafeEquals(this.id, ((TestEntity) obj).id);
+ }
+ return false;
+ }
+}
diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java
new file mode 100644
index 0000000000..2d5305dd61
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.cache.interceptor;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * Tests for {@link SimpleKeyGenerator} and {@link SimpleKey}.
+ *
+ * @author Phillip Webb
+ */
+public class SimpleKeyGeneratorTests {
+
+ private SimpleKeyGenerator generator = new SimpleKeyGenerator();
+
+ @Test
+ public void noValues() throws Exception {
+ Object k1 = generator.generate(null, null, new Object[] {});
+ Object k2 = generator.generate(null, null, new Object[] {});
+ Object k3 = generator.generate(null, null, new Object[] { "different" });
+ assertThat(k1.hashCode(), equalTo(k2.hashCode()));
+ assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
+ assertThat(k1, equalTo(k2));
+ assertThat(k1, not(equalTo(k3)));
+ }
+
+ @Test
+ public void singleValue() throws Exception {
+ Object k1 = generator.generate(null, null, new Object[] { "a" });
+ Object k2 = generator.generate(null, null, new Object[] { "a" });
+ Object k3 = generator.generate(null, null, new Object[] { "different" });
+ assertThat(k1.hashCode(), equalTo(k2.hashCode()));
+ assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
+ assertThat(k1, equalTo(k2));
+ assertThat(k1, not(equalTo(k3)));
+ assertThat(k1, equalTo((Object) "a"));
+ }
+
+ @Test
+ public void multipleValues() throws Exception {
+ Object k1 = generator.generate(null, null, new Object[] { "a", 1, "b" });
+ Object k2 = generator.generate(null, null, new Object[] { "a", 1, "b" });
+ Object k3 = generator.generate(null, null, new Object[] { "b", 1, "a" });
+ assertThat(k1.hashCode(), equalTo(k2.hashCode()));
+ assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
+ assertThat(k1, equalTo(k2));
+ assertThat(k1, not(equalTo(k3)));
+ }
+
+ @Test
+ public void singleNullValue() throws Exception {
+ Object k1 = generator.generate(null, null, new Object[] { null });
+ Object k2 = generator.generate(null, null, new Object[] { null });
+ Object k3 = generator.generate(null, null, new Object[] { "different" });
+ assertThat(k1.hashCode(), equalTo(k2.hashCode()));
+ assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
+ assertThat(k1, equalTo(k2));
+ assertThat(k1, not(equalTo(k3)));
+ assertThat(k1, instanceOf(SimpleKey.class));
+ }
+
+ @Test
+ public void multiplNullValues() throws Exception {
+ Object k1 = generator.generate(null, null, new Object[] { "a", null, "b", null });
+ Object k2 = generator.generate(null, null, new Object[] { "a", null, "b", null });
+ Object k3 = generator.generate(null, null, new Object[] { "a", null, "b" });
+ assertThat(k1.hashCode(), equalTo(k2.hashCode()));
+ assertThat(k1.hashCode(), not(equalTo(k3.hashCode())));
+ assertThat(k1, equalTo(k2));
+ assertThat(k1, not(equalTo(k3)));
+ }
+}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java
index e225839893..e2ca64e599 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,21 @@
package org.springframework.context.annotation;
-import example.scannable.DefaultNamedComponent;
-import org.junit.Test;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
+import example.scannable.DefaultNamedComponent;
import static org.junit.Assert.*;
/**
@@ -81,6 +86,22 @@ public class AnnotationBeanNameGeneratorTests {
assertEquals(expectedGeneratedBeanName, beanName);
}
+ @Test
+ public void testGenerateBeanNameFromMetaComponentWithStringValue() {
+ BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
+ AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(ComponentFromStringMeta.class);
+ String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
+ assertEquals("henry", beanName);
+ }
+
+ @Test
+ public void testGenerateBeanNameFromMetaComponentWithNonStringValue() {
+ BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
+ AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(ComponentFromNonStringMeta.class);
+ String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
+ assertEquals("annotationBeanNameGeneratorTests.ComponentFromNonStringMeta", beanName);
+ }
+
@Component("walden")
private static class ComponentWithName {
@@ -96,4 +117,19 @@ public class AnnotationBeanNameGeneratorTests {
private static class AnonymousComponent {
}
+ @Service("henry")
+ private static class ComponentFromStringMeta {
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ @Component
+ public @interface NonStringMetaComponent {
+ long value();
+ }
+
+ @NonStringMetaComponent(123)
+ private static class ComponentFromNonStringMeta {
+ }
+
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java
index 61b2612d29..e7795333e5 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/AsmCircularImportDetectionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,7 +41,8 @@ public class AsmCircularImportDetectionTests extends AbstractCircularImportDetec
new StandardEnvironment(),
new DefaultResourceLoader(),
new AnnotationBeanNameGenerator(),
- new DefaultListableBeanFactory());
+ new DefaultListableBeanFactory(),
+ null);
}
@Override
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java
index 49dfa4a966..d26e6b5f78 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java
@@ -16,55 +16,80 @@
package org.springframework.context.annotation;
-import static org.hamcrest.Matchers.equalTo;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
+import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
/**
- * Test for {@link Conditional} beans.
+ * Tests for {@link Conditional} beans.
*
* @author Phillip Webb
*/
+@SuppressWarnings("resource")
public class ConfigurationClassWithConditionTests {
+ private final AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+
@Rule
public ExpectedException thrown = ExpectedException.none();
+ @After
+ public void closeContext() {
+ ctx.close();
+ }
+
@Test
- public void conditionalOnBeanMatch() throws Exception {
- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ public void conditionalOnMissingBeanMatch() throws Exception {
ctx.register(BeanOneConfiguration.class, BeanTwoConfiguration.class);
ctx.refresh();
assertTrue(ctx.containsBean("bean1"));
assertFalse(ctx.containsBean("bean2"));
+ assertFalse(ctx.containsBean("configurationClassWithConditionTests.BeanTwoConfiguration"));
+ }
+
+ @Test
+ public void conditionalOnMissingBeanNoMatch() throws Exception {
+ ctx.register(BeanTwoConfiguration.class);
+ ctx.refresh();
+ assertFalse(ctx.containsBean("bean1"));
+ assertTrue(ctx.containsBean("bean2"));
+ assertTrue(ctx.containsBean("configurationClassWithConditionTests.BeanTwoConfiguration"));
+ }
+
+ @Test
+ public void conditionalOnBeanMatch() throws Exception {
+ ctx.register(BeanOneConfiguration.class, BeanThreeConfiguration.class);
+ ctx.refresh();
+ assertTrue(ctx.containsBean("bean1"));
+ assertTrue(ctx.containsBean("bean3"));
}
@Test
public void conditionalOnBeanNoMatch() throws Exception {
- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
- ctx.register(BeanTwoConfiguration.class);
+ ctx.register(BeanThreeConfiguration.class);
ctx.refresh();
- assertTrue(ctx.containsBean("bean2"));
+ assertFalse(ctx.containsBean("bean1"));
+ assertFalse(ctx.containsBean("bean3"));
}
@Test
public void metaConditional() throws Exception {
- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConfigurationWithMetaCondition.class);
ctx.refresh();
assertTrue(ctx.containsBean("bean"));
@@ -72,7 +97,6 @@ public class ConfigurationClassWithConditionTests {
@Test
public void nonConfigurationClass() throws Exception {
- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(NonConfigurationClass.class);
ctx.refresh();
thrown.expect(NoSuchBeanDefinitionException.class);
@@ -81,13 +105,38 @@ public class ConfigurationClassWithConditionTests {
@Test
public void methodConditional() throws Exception {
- AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ConditionOnMethodConfiguration.class);
ctx.refresh();
thrown.expect(NoSuchBeanDefinitionException.class);
assertNull(ctx.getBean(ExampleBean.class));
}
+ @Test
+ public void importsNotCreated() throws Exception {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.register(ImportsNotCreated.class);
+ ctx.refresh();
+ }
+
+ @Test
+ public void importsNotLoaded() throws Exception {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.register(ImportsNotLoaded.class);
+ ctx.refresh();
+ assertThat(ctx.containsBeanDefinition("a"), equalTo(false));
+ assertThat(ctx.containsBeanDefinition("b"), equalTo(false));
+ }
+
+ @Test
+ public void sensibleConditionContext() throws Exception {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.setResourceLoader(new DefaultResourceLoader());
+ ctx.setClassLoader(getClass().getClassLoader());
+ ctx.register(SensibleConditionContext.class);
+ ctx.refresh();
+ assertThat(ctx.getBean(ExampleBean.class), instanceOf(ExampleBean.class));
+ }
+
@Configuration
static class BeanOneConfiguration {
@Bean
@@ -105,6 +154,15 @@ public class ConfigurationClassWithConditionTests {
}
}
+ @Configuration
+ @Conditional(HasBeanOneCondition.class)
+ static class BeanThreeConfiguration {
+ @Bean
+ public ExampleBean bean3() {
+ return new ExampleBean();
+ }
+ }
+
@Configuration
@MetaConditional("test")
static class ConfigurationWithMetaCondition {
@@ -135,6 +193,19 @@ public class ConfigurationClassWithConditionTests {
}
}
+ static class HasBeanOneCondition implements ConfigurationCondition {
+
+ @Override
+ public ConfigurationPhase getConfigurationPhase() {
+ return ConfigurationPhase.REGISTER_BEAN;
+ }
+
+ @Override
+ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ return context.getBeanFactory().containsBeanDefinition("bean1");
+ }
+ }
+
static class MetaConditionalFilter implements Condition {
@Override
@@ -167,6 +238,112 @@ public class ConfigurationClassWithConditionTests {
}
}
+ @Configuration
+ @Never
+ @Import({ ConfigurationNotCreated.class, RegistrarNotCreated.class, ImportSelectorNotCreated.class })
+ static class ImportsNotCreated {
+ static {
+ if (true) throw new RuntimeException();
+ }
+ }
+
+ @Configuration
+ static class ConfigurationNotCreated {
+ static {
+ if (true) throw new RuntimeException();
+ }
+ }
+
+ static class RegistrarNotCreated implements ImportBeanDefinitionRegistrar {
+ static {
+ if (true) throw new RuntimeException();
+ }
+
+ @Override
+ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
+ BeanDefinitionRegistry registry) {
+ }
+ }
+
+ static class ImportSelectorNotCreated implements ImportSelector {
+
+ static {
+ if (true) throw new RuntimeException();
+ }
+
+ @Override
+ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
+ return new String[] {};
+ }
+
+ }
+
+ @Configuration
+ @Never
+ @Import({ ConfigurationNotLoaded.class, RegistrarNotLoaded.class, ImportSelectorNotLoaded.class })
+ static class ImportsNotLoaded {
+ static {
+ if (true) throw new RuntimeException();
+ }
+ }
+
+ @Configuration
+ static class ConfigurationNotLoaded {
+ @Bean
+ public String a() {
+ return "a";
+ }
+ }
+
+ static class RegistrarNotLoaded implements ImportBeanDefinitionRegistrar {
+ @Override
+ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
+ BeanDefinitionRegistry registry) {
+ throw new RuntimeException();
+ }
+ }
+
+ static class ImportSelectorNotLoaded implements ImportSelector {
+
+ @Override
+ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
+ return new String[] { SelectedConfigurationNotLoaded.class.getName() };
+ }
+
+ }
+
+ @Configuration
+ static class SelectedConfigurationNotLoaded {
+ @Bean
+ public String b() {
+ return "b";
+ }
+ }
+
+ @Configuration
+ @Conditional(SensibleConditionContextCondition.class)
+ static class SensibleConditionContext {
+ @Bean
+ ExampleBean exampleBean() {
+ return new ExampleBean();
+ }
+ }
+
+ static class SensibleConditionContextCondition implements Condition {
+
+ @Override
+ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
+ assertThat(context.getApplicationContext(), notNullValue());
+ assertThat(context.getBeanFactory(), notNullValue());
+ assertThat(context.getClassLoader(), notNullValue());
+ assertThat(context.getEnvironment(), notNullValue());
+ assertThat(context.getRegistry(), notNullValue());
+ assertThat(context.getResourceLoader(), notNullValue());
+ return true;
+ }
+
+ }
+
static class ExampleBean {
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrarTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrarTests.java
index 6f55b1f669..6a45feb0d5 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrarTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/ImportBeanDefinitionRegistrarTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,26 +16,27 @@
package org.springframework.context.annotation;
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Test;
-
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSource;
import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
/**
* Integration tests for {@link ImportBeanDefinitionRegistrar}.
*
@@ -53,6 +54,7 @@ public class ImportBeanDefinitionRegistrarTests {
assertThat(SampleRegistrar.beanFactory, is((BeanFactory) context.getBeanFactory()));
assertThat(SampleRegistrar.classLoader, is(context.getBeanFactory().getBeanClassLoader()));
assertThat(SampleRegistrar.resourceLoader, is(notNullValue()));
+ assertThat(SampleRegistrar.environment, is((Environment) context.getEnvironment()));
}
@Sample
@@ -69,11 +71,12 @@ public class ImportBeanDefinitionRegistrarTests {
}
static class SampleRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware, ResourceLoaderAware,
- BeanFactoryAware {
+ BeanFactoryAware, EnvironmentAware {
static ClassLoader classLoader;
static ResourceLoader resourceLoader;
static BeanFactory beanFactory;
+ static Environment environment;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
@@ -90,6 +93,11 @@ public class ImportBeanDefinitionRegistrarTests {
SampleRegistrar.resourceLoader = resourceLoader;
}
+ @Override
+ public void setEnvironment(Environment environment) {
+ SampleRegistrar.environment = environment;
+ }
+
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java
index abdfcdbf6d..b15e54221c 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/ImportSelectorTests.java
@@ -16,11 +16,6 @@
package org.springframework.context.annotation;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.spy;
-
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -28,12 +23,27 @@ import java.lang.annotation.Target;
import org.junit.Test;
import org.mockito.InOrder;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.MessageSource;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrarTests.SampleRegistrar;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
/**
* Tests for {@link ImportSelector} and {@link DeferredImportSelector}.
*
@@ -50,10 +60,62 @@ public class ImportSelectorTests {
context.refresh();
context.getBean(Config.class);
InOrder ordered = inOrder(beanFactory);
- ordered.verify(beanFactory).registerBeanDefinition(eq("a"), any(BeanDefinition.class));
- ordered.verify(beanFactory).registerBeanDefinition(eq("b"), any(BeanDefinition.class));
- ordered.verify(beanFactory).registerBeanDefinition(eq("d"), any(BeanDefinition.class));
- ordered.verify(beanFactory).registerBeanDefinition(eq("c"), any(BeanDefinition.class));
+ ordered.verify(beanFactory).registerBeanDefinition(eq("a"), (BeanDefinition) anyObject());
+ ordered.verify(beanFactory).registerBeanDefinition(eq("b"), (BeanDefinition) anyObject());
+ ordered.verify(beanFactory).registerBeanDefinition(eq("d"), (BeanDefinition) anyObject());
+ ordered.verify(beanFactory).registerBeanDefinition(eq("c"), (BeanDefinition) anyObject());
+ }
+
+ @Test
+ public void invokeAwareMethodsInImportSelector() {
+
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class);
+ context.getBean(MessageSource.class);
+
+ assertThat(SampleRegistrar.beanFactory, is((BeanFactory) context.getBeanFactory()));
+ assertThat(SampleRegistrar.classLoader, is(context.getBeanFactory().getBeanClassLoader()));
+ assertThat(SampleRegistrar.resourceLoader, is(notNullValue()));
+ assertThat(SampleRegistrar.environment, is((Environment) context.getEnvironment()));
+ }
+
+ @Configuration
+ @Import(SampleImportSelector.class)
+ static class AwareConfig {
+
+ }
+
+ static class SampleImportSelector implements ImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
+ BeanFactoryAware, EnvironmentAware {
+
+ static ClassLoader classLoader;
+ static ResourceLoader resourceLoader;
+ static BeanFactory beanFactory;
+ static Environment environment;
+
+ @Override
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ SampleRegistrar.classLoader = classLoader;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ SampleRegistrar.beanFactory = beanFactory;
+ }
+
+ @Override
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ SampleRegistrar.resourceLoader = resourceLoader;
+ }
+
+ @Override
+ public void setEnvironment(Environment environment) {
+ SampleRegistrar.environment = environment;
+ }
+
+ @Override
+ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
+ return new String[] {};
+ }
}
@Sample
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/componentscan/simple/SimpleComponent.java b/spring-context/src/test/java/org/springframework/context/annotation/componentscan/simple/SimpleComponent.java
index 956b90d157..9d63ad60a2 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/componentscan/simple/SimpleComponent.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/componentscan/simple/SimpleComponent.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,15 @@
package org.springframework.context.annotation.componentscan.simple;
+import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class SimpleComponent {
+ @Bean
+ public String exampleBean() {
+ return "example";
+ }
+
}
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java
index 52aaaac7e9..dcffc2c2ed 100644
--- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java
+++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ScopingTests.java
@@ -193,8 +193,8 @@ public class ScopingTests {
@Test
public void testScopedConfigurationBeanDefinitionCount() throws Exception {
// count the beans
- // 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry
- assertEquals(10, ctx.getBeanDefinitionCount());
+ // 6 @Beans + 1 Configuration + 2 scoped proxy + 1 importRegistry + 1 enhanced config post processor
+ assertEquals(11, ctx.getBeanDefinitionCount());
}
// /**
diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java
new file mode 100644
index 0000000000..d59e4133d5
--- /dev/null
+++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.annotation.configuration;
+
+import org.junit.Test;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests for SPR-10668.
+ *
+ * @author Oliver Gierke
+ * @author Phillip Webb
+ */
+public class Spr10668Tests {
+
+ @Test
+ public void testSelfInjectHierarchy() throws Exception {
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
+ ChildConfig.class);
+ assertNotNull(context.getBean(MyComponent.class));
+ context.close();
+ }
+
+ @Configuration
+ public static class ParentConfig implements BeanFactoryAware {
+
+ @Autowired(required = false)
+ MyComponent component;
+
+ public ParentConfig() {
+ System.out.println("Parent " + getClass());
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ System.out.println("BFA " + getClass());
+ }
+
+ }
+
+ @Configuration
+ public static class ChildConfig extends ParentConfig {
+
+ @Bean
+ public MyComponentImpl myComponent() {
+ return new MyComponentImpl();
+ }
+
+ }
+
+ public static interface MyComponent {
+ }
+
+ public static class MyComponentImpl implements MyComponent {
+ }
+}
diff --git a/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java b/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java
index f8f13285c8..9da942c3ed 100644
--- a/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java
+++ b/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java
@@ -265,4 +265,23 @@ public class PropertySourcesPlaceholderConfigurerTests {
thrown.expect(IllegalStateException.class);
ppc.getAppliedPropertySources();
}
+
+ @Test
+ public void multipleLocationsWithDefaultResolvedValue() throws Exception {
+ // SPR-10619
+ PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
+ ClassPathResource doesNotHave = new ClassPathResource("test.properties", getClass());
+ ClassPathResource setToTrue = new ClassPathResource("placeholder.properties", getClass());
+ ppc.setLocations(new Resource[] { doesNotHave, setToTrue });
+ ppc.setIgnoreResourceNotFound(true);
+ ppc.setIgnoreUnresolvablePlaceholders(true);
+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+ bf.registerBeanDefinition("testBean",
+ genericBeanDefinition(TestBean.class)
+ .addPropertyValue("jedi", "${jedi:false}")
+ .getBeanDefinition());
+ ppc.postProcessBeanFactory(bf);
+ assertThat(bf.getBean(TestBean.class).isJedi(), equalTo(true));
+ }
+
}
diff --git a/spring-context/src/test/java/org/springframework/context/support/placeholder.properties b/spring-context/src/test/java/org/springframework/context/support/placeholder.properties
index 6c9d097621..11f0d7f030 100644
--- a/spring-context/src/test/java/org/springframework/context/support/placeholder.properties
+++ b/spring-context/src/test/java/org/springframework/context/support/placeholder.properties
@@ -1,3 +1,4 @@
targetName=wrappedAssemblerOne
logicName=logicTwo
realLogicName=realLogic
+jedi=true
diff --git a/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml b/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml
index 5ebf54ddcb..f13c780916 100644
--- a/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml
+++ b/spring-context/src/test/resources/org/springframework/cache/config/cache-advice.xml
@@ -45,6 +45,7 @@
+
@@ -82,6 +83,7 @@
+
diff --git a/spring-core/src/main/java/org/springframework/core/io/PathResource.java b/spring-core/src/main/java/org/springframework/core/io/PathResource.java
new file mode 100644
index 0000000000..5a2c9f93b5
--- /dev/null
+++ b/spring-core/src/main/java/org/springframework/core/io/PathResource.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.core.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.springframework.util.Assert;
+
+/**
+ * {@link Resource} implementation for {@code java.nio.file.Path} handles.
+ * Supports resolution as File, and also as URL.
+ * Implements the extended {@link WritableResource} interface.
+ *
+ * @author Philippe Marschall
+ * @since 4.0
+ * @see java.nio.file.Path
+ */
+public class PathResource extends AbstractResource implements WritableResource {
+
+ private final Path path;
+
+
+ /**
+ * Create a new PathResource from a Path handle.
+ * Note: Unlike {@link FileSystemResource}, when building relative resources
+ * via {@link #createRelative}, the relative path will be built underneath the
+ * given root:
+ * e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
+ * @param path a Path handle
+ */
+ public PathResource(Path path) {
+ Assert.notNull(path, "Path must not be null");
+ this.path = path.normalize();
+ }
+
+ /**
+ * Create a new PathResource from a Path handle.
+ *
Note: Unlike {@link FileSystemResource}, when building relative resources
+ * via {@link #createRelative}, the relative path will be built underneath the
+ * given root:
+ * e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
+ * @param path a path
+ * @see java.nio.file.Paths#get(String, String...)
+ */
+ public PathResource(String path) {
+ Assert.notNull(path, "Path must not be null");
+ this.path = Paths.get(path).normalize();
+ }
+
+ /**
+ * Create a new PathResource from a Path handle.
+ *
Note: Unlike {@link FileSystemResource}, when building relative resources
+ * via {@link #createRelative}, the relative path will be built underneath the
+ * given root:
+ * e.g. Paths.get("C:/dir1/"), relative path "dir2" -> "C:/dir1/dir2"!
+ * @see java.nio.file.Paths#get(URI)
+ * @param uri a path URI
+ */
+ public PathResource(URI uri) {
+ Assert.notNull(uri, "URI must not be null");
+ this.path = Paths.get(uri).normalize();
+ }
+
+
+ /**
+ * Return the file path for this resource.
+ */
+ public final String getPath() {
+ return this.path.toString();
+ }
+
+ /**
+ * This implementation returns whether the underlying file exists.
+ * @see org.springframework.core.io.PathResource#exists()
+ */
+ @Override
+ public boolean exists() {
+ return Files.exists(this.path);
+ }
+
+ /**
+ * This implementation checks whether the underlying file is marked as readable
+ * (and corresponds to an actual file with content, not to a directory).
+ * @see java.nio.file.Files#isReadable(Path)
+ * @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
+ */
+ @Override
+ public boolean isReadable() {
+ return (Files.isReadable(this.path) && !Files.isDirectory(this.path));
+ }
+
+ /**
+ * This implementation opens a InputStream for the underlying file.
+ * @see java.nio.file.spi.FileSystemProvider#newInputStream(Path, OpenOption...)
+ */
+ @Override
+ public InputStream getInputStream() throws IOException {
+ if(!exists()) {
+ throw new FileNotFoundException(getPath() + " (No such file or directory)");
+ }
+ if(Files.isDirectory(this.path)) {
+ throw new FileNotFoundException(getPath() + " (Is a directory)");
+ }
+ return Files.newInputStream(this.path);
+ }
+
+ /**
+ * This implementation returns a URL for the underlying file.
+ * @see java.nio.file.Path#toUri()
+ * @see java.net.URI#toURL()
+ */
+ @Override
+ public URL getURL() throws IOException {
+ return this.path.toUri().toURL();
+ }
+
+ /**
+ * This implementation returns a URI for the underlying file.
+ * @see java.nio.file.Path#toUri()
+ */
+ @Override
+ public URI getURI() throws IOException {
+ return this.path.toUri();
+ }
+
+ /**
+ * This implementation returns the underlying File reference.
+ */
+ @Override
+ public File getFile() throws IOException {
+ try {
+ return this.path.toFile();
+ }
+ catch (UnsupportedOperationException ex) {
+ // only Paths on the default file system can be converted to a File
+ // do exception translation for cases where conversion is not possible
+ throw new FileNotFoundException(this.path + " cannot be resolved to "
+ + "absolute file path");
+ }
+ }
+
+ /**
+ * This implementation returns the underlying File's length.
+ */
+ @Override
+ public long contentLength() throws IOException {
+ return Files.size(this.path);
+ }
+
+ /**
+ * This implementation returns the underlying File's timestamp.
+ * @see java.nio.file.Files#getLastModifiedTime(Path, java.nio.file.LinkOption...)
+ */
+ @Override
+ public long lastModified() throws IOException {
+ // we can not use the super class method since it uses conversion to a File and
+ // only Paths on the default file system can be converted to a File
+ return Files.getLastModifiedTime(path).toMillis();
+ }
+
+ /**
+ * This implementation creates a FileResource, applying the given path
+ * relative to the path of the underlying file of this resource descriptor.
+ * @see java.nio.file.Path#resolve(String)
+ */
+ @Override
+ public Resource createRelative(String relativePath) throws IOException {
+ return new PathResource(this.path.resolve(relativePath));
+ }
+
+ /**
+ * This implementation returns the name of the file.
+ * @see java.nio.file.Path#getFileName()
+ */
+ @Override
+ public String getFilename() {
+ return this.path.getFileName().toString();
+ }
+
+ @Override
+ public String getDescription() {
+ return "path [" + this.path.toAbsolutePath() + "]";
+ }
+
+ // implementation of WritableResource
+
+ /**
+ * This implementation checks whether the underlying file is marked as writable
+ * (and corresponds to an actual file with content, not to a directory).
+ * @see java.nio.file.Files#isWritable(Path)
+ * @see java.nio.file.Files#isDirectory(Path, java.nio.file.LinkOption...)
+ */
+ @Override
+ public boolean isWritable() {
+ return Files.isWritable(this.path) && !Files.isDirectory(this.path);
+ }
+
+ /**
+ * This implementation opens a OutputStream for the underlying file.
+ * @see java.nio.file.spi.FileSystemProvider#newOutputStream(Path, OpenOption...)
+ */
+ @Override
+ public OutputStream getOutputStream() throws IOException {
+ if(Files.isDirectory(this.path)) {
+ throw new FileNotFoundException(getPath() + " (Is a directory)");
+ }
+ return Files.newOutputStream(this.path);
+ }
+
+
+ /**
+ * This implementation compares the underlying Path references.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return (obj == this ||
+ (obj instanceof PathResource && this.path.equals(((PathResource) obj).path)));
+ }
+
+ /**
+ * This implementation returns the hash code of the underlying Path reference.
+ */
+ @Override
+ public int hashCode() {
+ return this.path.hashCode();
+ }
+
+}
diff --git a/spring-core/src/main/java/org/springframework/core/io/Resource.java b/spring-core/src/main/java/org/springframework/core/io/Resource.java
index cc6eef308f..e3fc2a4e59 100644
--- a/spring-core/src/main/java/org/springframework/core/io/Resource.java
+++ b/spring-core/src/main/java/org/springframework/core/io/Resource.java
@@ -42,6 +42,7 @@ import java.net.URL;
* @see UrlResource
* @see ByteArrayResource
* @see InputStreamResource
+ * @see PathResource
*/
public interface Resource extends InputStreamSource {
diff --git a/spring-core/src/main/java/org/springframework/util/SocketUtils.java b/spring-core/src/main/java/org/springframework/util/SocketUtils.java
new file mode 100644
index 0000000000..4f6958ca71
--- /dev/null
+++ b/spring-core/src/main/java/org/springframework/util/SocketUtils.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.util;
+
+import java.net.DatagramSocket;
+import java.net.ServerSocket;
+import java.util.Random;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.net.ServerSocketFactory;
+
+/**
+ * Simple utility methods for working with network sockets — for example,
+ * for finding available ports on {@code localhost}.
+ *
+ *
Within this class, a TCP port refers to a port for a {@link ServerSocket};
+ * whereas, a UDP port refers to a port for a {@link DatagramSocket}.
+ *
+ * @author Sam Brannen
+ * @author Ben Hale
+ * @author Arjen Poutsma
+ * @author Gunnar Hillert
+ * @since 4.0
+ */
+public final class SocketUtils {
+
+ /**
+ * The default minimum value for port ranges used when finding an available
+ * socket port.
+ */
+ public static final int PORT_RANGE_MIN = 1024;
+
+ /**
+ * The default maximum value for port ranges used when finding an available
+ * socket port.
+ */
+ public static final int PORT_RANGE_MAX = 65535;
+
+ private static final Random random = new Random(System.currentTimeMillis());
+
+
+ /**
+ * Although {@code SocketUtils} consists solely of static utility methods,
+ * this constructor is intentionally {@code public}.
+ *
+ *
Rationale
+ *
+ * Static methods from this class may be invoked from within XML
+ * configuration files using the Spring Expression Language (SpEL) and the
+ * following syntax.
+ *
+ *
<bean id="bean1" ... p:port="#{T(org.springframework.util.SocketUtils).findAvailableTcpPort(12000)}" />
+ *
+ * If this constructor were {@code private}, you would be required to supply
+ * the fully qualified class name to SpEL's {@code T()} function for each usage.
+ * Thus, the fact that this constructor is {@code public} allows you to reduce
+ * boilerplate configuration with SpEL as can be seen in the following example.
+ *
+ * <bean id="socketUtils" class="org.springframework.util.SocketUtils" />
+ *
+ *<bean id="bean1" ... p:port="#{socketUtils.findAvailableTcpPort(12000)}" />
+ *
+ *<bean id="bean2" ... p:port="#{socketUtils.findAvailableTcpPort(30000)}" />
+ */
+ public SocketUtils() {
+ /* no-op */
+ }
+
+ /**
+ * Find an available TCP port randomly selected from the range
+ * [{@value #PORT_RANGE_MIN}, {@value #PORT_RANGE_MAX}].
+ *
+ * @return an available TCP port number
+ * @throws IllegalStateException if no available port could be found
+ */
+ public static int findAvailableTcpPort() {
+ return findAvailableTcpPort(PORT_RANGE_MIN);
+ }
+
+ /**
+ * Find an available TCP port randomly selected from the range
+ * [{@code minPort}, {@value #PORT_RANGE_MAX}].
+ *
+ * @param minPort the minimum port number
+ * @return an available TCP port number
+ * @throws IllegalStateException if no available port could be found
+ */
+ public static int findAvailableTcpPort(int minPort) {
+ return findAvailableTcpPort(minPort, PORT_RANGE_MAX);
+ }
+
+ /**
+ * Find an available TCP port randomly selected from the range
+ * [{@code minPort}, {@code maxPort}].
+ *
+ * @param minPort the minimum port number
+ * @param maxPort the maximum port number
+ * @return an available TCP port number
+ * @throws IllegalStateException if no available port could be found
+ */
+ public static int findAvailableTcpPort(int minPort, int maxPort) {
+ return SocketType.TCP.findAvailablePort(minPort, maxPort);
+ }
+
+ /**
+ * Find the requested number of available TCP ports, each randomly selected
+ * from the range [{@value #PORT_RANGE_MIN}, {@value #PORT_RANGE_MAX}].
+ *
+ * @param numRequested the number of available ports to find
+ * @return a sorted set of available TCP port numbers
+ * @throws IllegalStateException if the requested number of available ports could not be found
+ */
+ public static SortedSet findAvailableTcpPorts(int numRequested) {
+ return findAvailableTcpPorts(numRequested, PORT_RANGE_MIN, PORT_RANGE_MAX);
+ }
+
+ /**
+ * Find the requested number of available TCP ports, each randomly selected
+ * from the range [{@code minPort}, {@code maxPort}].
+ *
+ * @param numRequested the number of available ports to find
+ * @param minPort the minimum port number
+ * @param maxPort the maximum port number
+ * @return a sorted set of available TCP port numbers
+ * @throws IllegalStateException if the requested number of available ports could not be found
+ */
+ public static SortedSet findAvailableTcpPorts(int numRequested, int minPort, int maxPort) {
+ return SocketType.TCP.findAvailablePorts(numRequested, minPort, maxPort);
+ }
+
+ /**
+ * Find an available UDP port randomly selected from the range
+ * [{@value #PORT_RANGE_MIN}, {@value #PORT_RANGE_MAX}].
+ *
+ * @return an available UDP port number
+ * @throws IllegalStateException if no available port could be found
+ */
+ public static int findAvailableUdpPort() {
+ return findAvailableUdpPort(PORT_RANGE_MIN);
+ }
+
+ /**
+ * Find an available UDP port randomly selected from the range
+ * [{@code minPort}, {@value #PORT_RANGE_MAX}].
+ *
+ * @param minPort the minimum port number
+ * @return an available UDP port number
+ * @throws IllegalStateException if no available port could be found
+ */
+ public static int findAvailableUdpPort(int minPort) {
+ return findAvailableUdpPort(minPort, PORT_RANGE_MAX);
+ }
+
+ /**
+ * Find an available UDP port randomly selected from the range
+ * [{@code minPort}, {@code maxPort}].
+ *
+ * @param minPort the minimum port number
+ * @param maxPort the maximum port number
+ * @return an available UDP port number
+ * @throws IllegalStateException if no available port could be found
+ */
+ public static int findAvailableUdpPort(int minPort, int maxPort) {
+ return SocketType.UDP.findAvailablePort(minPort, maxPort);
+ }
+
+ /**
+ * Find the requested number of available UDP ports, each randomly selected
+ * from the range [{@value #PORT_RANGE_MIN}, {@value #PORT_RANGE_MAX}].
+ *
+ * @param numRequested the number of available ports to find
+ * @return a sorted set of available UDP port numbers
+ * @throws IllegalStateException if the requested number of available ports could not be found
+ */
+ public static SortedSet findAvailableUdpPorts(int numRequested) {
+ return findAvailableUdpPorts(numRequested, PORT_RANGE_MIN, PORT_RANGE_MAX);
+ }
+
+ /**
+ * Find the requested number of available UDP ports, each randomly selected
+ * from the range [{@code minPort}, {@code maxPort}].
+ *
+ * @param numRequested the number of available ports to find
+ * @param minPort the minimum port number
+ * @param maxPort the maximum port number
+ * @return a sorted set of available UDP port numbers
+ * @throws IllegalStateException if the requested number of available ports could not be found
+ */
+ public static SortedSet findAvailableUdpPorts(int numRequested, int minPort, int maxPort) {
+ return SocketType.UDP.findAvailablePorts(numRequested, minPort, maxPort);
+ }
+
+
+ private static enum SocketType {
+
+ TCP {
+
+ @Override
+ protected boolean isPortAvailable(int port) {
+ try {
+ ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port);
+ serverSocket.close();
+ return true;
+ }
+ catch (Exception ex) {
+ return false;
+ }
+ }
+ },
+
+ UDP {
+
+ @Override
+ protected boolean isPortAvailable(int port) {
+ try {
+ DatagramSocket socket = new DatagramSocket(port);
+ socket.close();
+ return true;
+ }
+ catch (Exception ex) {
+ return false;
+ }
+ }
+ };
+
+ /**
+ * Determine if the specified port for this {@code SocketType} is
+ * currently available on {@code localhost}.
+ */
+ protected abstract boolean isPortAvailable(int port);
+
+ /**
+ * Find a pseudo-random port number within the range
+ * [{@code minPort}, {@code maxPort}].
+ *
+ * @param minPort the minimum port number
+ * @param maxPort the maximum port number
+ * @return a random port number within the specified range
+ */
+ private int findRandomPort(int minPort, int maxPort) {
+ int portRange = maxPort - minPort;
+ return minPort + random.nextInt(portRange);
+ }
+
+ /**
+ * Find an available port for this {@code SocketType}, randomly selected
+ * from the range [{@code minPort}, {@code maxPort}].
+ *
+ * @param minPort the minimum port number
+ * @param maxPort the maximum port number
+ * @return an available port number for this socket type
+ * @throws IllegalStateException if no available port could be found
+ */
+ int findAvailablePort(int minPort, int maxPort) {
+ Assert.isTrue(minPort > 0, "'minPort' must be greater than 0");
+ Assert.isTrue(maxPort > minPort, "'maxPort' must be greater than 'minPort'");
+ Assert.isTrue(maxPort <= PORT_RANGE_MAX, "'maxPort' must be less than or equal to " + PORT_RANGE_MAX);
+
+ int portRange = maxPort - minPort;
+ int candidatePort;
+ int searchCounter = 0;
+ do {
+ if (++searchCounter > portRange) {
+ throw new IllegalStateException(String.format(
+ "Could not find an available %s port in the range [%d, %d] after %d attempts", name(), minPort,
+ maxPort, searchCounter));
+ }
+ candidatePort = findRandomPort(minPort, maxPort);
+ } while (!isPortAvailable(candidatePort));
+
+ return candidatePort;
+ }
+
+ /**
+ * Find the requested number of available ports for this {@code SocketType},
+ * each randomly selected from the range [{@code minPort}, {@code maxPort}].
+ *
+ * @param numRequested the number of available ports to find
+ * @param minPort the minimum port number
+ * @param maxPort the maximum port number
+ * @return a sorted set of available port numbers for this socket type
+ * @throws IllegalStateException if the requested number of available ports could not be found
+ */
+ SortedSet findAvailablePorts(int numRequested, int minPort, int maxPort) {
+ Assert.isTrue(minPort > 0, "'minPort' must be greater than 0");
+ Assert.isTrue(maxPort > minPort, "'maxPort' must be greater than 'minPort'");
+ Assert.isTrue(maxPort <= PORT_RANGE_MAX, "'maxPort' must be less than or equal to " + PORT_RANGE_MAX);
+ Assert.isTrue(numRequested > 0, "'numRequested' must be greater than 0");
+ Assert.isTrue((maxPort - minPort) >= numRequested,
+ "'numRequested' must not be greater than 'maxPort' - 'minPort'");
+
+ final SortedSet availablePorts = new TreeSet();
+ int attemptCount = 0;
+ while ((++attemptCount <= numRequested + 100) && (availablePorts.size() < numRequested)) {
+ availablePorts.add(findAvailablePort(minPort, maxPort));
+ }
+
+ if (availablePorts.size() != numRequested) {
+ throw new IllegalStateException(String.format(
+ "Could not find %d available %s ports in the range [%d, %d]", numRequested, name(), minPort,
+ maxPort));
+ }
+
+ return availablePorts;
+ }
+ }
+
+}
diff --git a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
index 5bdc32d9a2..fecad196da 100644
--- a/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
+++ b/spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,10 +65,13 @@ public class GenericTypeResolverTests {
@Test
public void methodReturnTypes() {
- assertEquals(Integer.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
- assertEquals(String.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
+ assertEquals(Integer.class,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
+ assertEquals(String.class,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "raw"), MyInterfaceType.class));
- assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
+ assertEquals(null,
+ resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
}
/**
@@ -135,20 +138,22 @@ public class GenericTypeResolverTests {
*/
@Test
public void testResolveType() {
- Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
- MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
- assertEquals(MyInterfaceType.class,
- resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap()));
+ Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
+ MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
+ assertEquals(MyInterfaceType.class,
+ resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap()));
- Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage", MyInterfaceType[].class);
- MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
- assertEquals(MyInterfaceType[].class,
- resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap()));
+ Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage",
+ MyInterfaceType[].class);
+ MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
+ assertEquals(MyInterfaceType[].class,
+ resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap()));
- Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage", Object[].class);
- MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
- Map varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
- assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
+ Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage",
+ Object[].class);
+ MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
+ Map varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
+ assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
}
@@ -171,26 +176,44 @@ public class GenericTypeResolverTests {
}
public static class MyTypeWithMethods {
- public MyInterfaceType integer() { return null; }
- public MySimpleInterfaceType string() { return null; }
- public Object object() { return null; }
+
+ public MyInterfaceType integer() {
+ return null;
+ }
+
+ public MySimpleInterfaceType string() {
+ return null;
+ }
+
+ public Object object() {
+ return null;
+ }
+
@SuppressWarnings("rawtypes")
- public MyInterfaceType raw() { return null; }
- public String notParameterized() { return null; }
- public String notParameterizedWithArguments(Integer x, Boolean b) { return null; }
+ public MyInterfaceType raw() {
+ return null;
+ }
+
+ public String notParameterized() {
+ return null;
+ }
+
+ public String notParameterizedWithArguments(Integer x, Boolean b) {
+ return null;
+ }
/**
- * Simulates a factory method that wraps the supplied object in a proxy
- * of the same type.
+ * Simulates a factory method that wraps the supplied object in a proxy of the
+ * same type.
*/
public static T createProxy(T object) {
return null;
}
/**
- * Similar to {@link #createProxy(Object)} but adds an additional argument
- * before the argument of type {@code T}. Note that they may potentially
- * be of the same time when invoked!
+ * Similar to {@link #createProxy(Object)} but adds an additional argument before
+ * the argument of type {@code T}. Note that they may potentially be of the same
+ * time when invoked!
*/
public static T createNamedProxy(String name, T object) {
return null;
@@ -204,8 +227,8 @@ public class GenericTypeResolverTests {
}
/**
- * Similar to {@link #createMock(Class)} but adds an additional method
- * argument before the parameterized argument.
+ * Similar to {@link #createMock(Class)} but adds an additional method argument
+ * before the parameterized argument.
*/
public static T createNamedMock(String name, Class toMock) {
return null;
@@ -220,8 +243,8 @@ public class GenericTypeResolverTests {
}
/**
- * Extract some value of the type supported by the interface (i.e., by
- * a concrete, non-generic implementation of the interface).
+ * Extract some value of the type supported by the interface (i.e., by a concrete,
+ * non-generic implementation of the interface).
*/
public static T extractValueFrom(MyInterfaceType myInterfaceType) {
return null;
diff --git a/spring-core/src/test/java/org/springframework/core/io/PathResourceTests.java b/spring-core/src/test/java/org/springframework/core/io/PathResourceTests.java
new file mode 100644
index 0000000000..a4f87b638f
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/core/io/PathResourceTests.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2002-2012 the original author 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.core.io;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.springframework.util.FileCopyUtils;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+import static org.mockito.BDDMockito.*;
+
+/**
+ * Unit tests for the {@link PathResource} class.
+ *
+ * @author Philippe Marschall
+ * @author Phillip Webb
+ */
+public class PathResourceTests {
+
+ private static final String TEST_DIR = "src/test/java/org/springframework/core/io";
+
+ private static final String TEST_FILE = "src/test/java/org/springframework/core/io/example.properties";
+
+ private static final String NON_EXISTING_FILE = "src/test/java/org/springframework/core/io/doesnotexist.properties";
+
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+
+ @Test
+ public void nullPath() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Path must not be null");
+ new PathResource((Path) null);
+ }
+
+ @Test
+ public void nullPathString() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Path must not be null");
+ new PathResource((String) null);
+ }
+
+ @Test
+ public void nullUri() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("URI must not be null");
+ new PathResource((URI) null);
+ }
+
+ @Test
+ public void createFromPath() throws Exception {
+ Path path = Paths.get(TEST_FILE);
+ PathResource resource = new PathResource(path);
+ assertThat(resource.getPath(), equalTo(TEST_FILE));
+ }
+
+ @Test
+ public void createFromString() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ assertThat(resource.getPath(), equalTo(TEST_FILE));
+ }
+
+ @Test
+ public void createFromUri() throws Exception {
+ File file = new File(TEST_FILE);
+ PathResource resource = new PathResource(file.toURI());
+ assertThat(resource.getPath(), equalTo(file.getAbsoluteFile().toString()));
+ }
+
+ @Test
+ public void getPathForFile() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ assertThat(resource.getPath(), equalTo(TEST_FILE));
+ }
+
+ @Test
+ public void getPathForDir() throws Exception {
+ PathResource resource = new PathResource(TEST_DIR);
+ assertThat(resource.getPath(), equalTo(TEST_DIR));
+ }
+
+ @Test
+ public void fileExists() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ assertThat(resource.exists(), equalTo(true));
+ }
+
+ @Test
+ public void dirExists() throws Exception {
+ PathResource resource = new PathResource(TEST_DIR);
+ assertThat(resource.exists(), equalTo(true));
+ }
+
+ @Test
+ public void fileDoesNotExist() throws Exception {
+ PathResource resource = new PathResource(NON_EXISTING_FILE);
+ assertThat(resource.exists(), equalTo(false));
+ }
+
+ @Test
+ public void fileIsReadable() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ assertThat(resource.isReadable(), equalTo(true));
+ }
+
+ @Test
+ public void doesNotExistIsNotReadable() throws Exception {
+ PathResource resource = new PathResource(NON_EXISTING_FILE);
+ assertThat(resource.isReadable(), equalTo(false));
+ }
+
+ @Test
+ public void directoryIsNotReadable() throws Exception {
+ PathResource resource = new PathResource(TEST_DIR);
+ assertThat(resource.isReadable(), equalTo(false));
+ }
+
+ @Test
+ public void getInputStream() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
+ assertThat(bytes.length, greaterThan(0));
+ }
+
+ @Test
+ public void getInputStreamForDir() throws Exception {
+ PathResource resource = new PathResource(TEST_DIR);
+ thrown.expect(FileNotFoundException.class);
+ resource.getInputStream();
+ }
+
+ @Test
+ public void getInputStreamDoesNotExist() throws Exception {
+ PathResource resource = new PathResource(NON_EXISTING_FILE);
+ thrown.expect(FileNotFoundException.class);
+ resource.getInputStream();
+ }
+
+ @Test
+ public void getUrl() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ assertTrue(resource.getURL().toString().endsWith(TEST_FILE));
+ }
+
+ @Test
+ public void getUri() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ assertTrue(resource.getURI().toString().endsWith(TEST_FILE));
+ }
+
+ @Test
+ public void getFile() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ File file = new File(TEST_FILE);
+ assertThat(resource.getFile().getAbsoluteFile(), equalTo(file.getAbsoluteFile()));
+ }
+
+ @Test
+ public void getFileUnsupported() throws Exception {
+ Path path = mock(Path.class);
+ given(path.normalize()).willReturn(path);
+ given(path.toFile()).willThrow(new UnsupportedOperationException());
+ PathResource resource = new PathResource(path);
+ thrown.expect(FileNotFoundException.class);
+ resource.getFile();
+ }
+
+ @Test
+ public void contentLength() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ File file = new File(TEST_FILE);
+ assertThat(resource.contentLength(), equalTo(file.length()));
+ }
+
+ @Test
+ public void contentLengthForDirectory() throws Exception {
+ PathResource resource = new PathResource(TEST_DIR);
+ File file = new File(TEST_DIR);
+ assertThat(resource.contentLength(), equalTo(file.length()));
+ }
+
+ @Test
+ public void lastModified() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ File file = new File(TEST_FILE);
+ assertThat(resource.lastModified(), equalTo(file.lastModified()));
+ }
+
+ @Test
+ public void createRelativeFromDir() throws Exception {
+ Resource resource = new PathResource(TEST_DIR).createRelative("example.properties");
+ assertThat(resource, equalTo((Resource) new PathResource(TEST_FILE)));
+ }
+
+ @Test
+ public void createRelativeFromFile() throws Exception {
+ Resource resource = new PathResource(TEST_FILE).createRelative("../example.properties");
+ assertThat(resource, equalTo((Resource) new PathResource(TEST_FILE)));
+ }
+
+ @Test
+ public void filename() throws Exception {
+ Resource resource = new PathResource(TEST_FILE);
+ assertThat(resource.getFilename(), equalTo("example.properties"));
+ }
+
+ @Test
+ public void description() throws Exception {
+ Resource resource = new PathResource(TEST_FILE);
+ assertThat(resource.getDescription(), containsString("path ["));
+ assertThat(resource.getDescription(), containsString(TEST_FILE));
+ }
+
+ @Test
+ public void fileIsWritable() throws Exception {
+ PathResource resource = new PathResource(TEST_FILE);
+ assertThat(resource.isWritable(), equalTo(true));
+ }
+
+ @Test
+ public void directoryIsNotWritable() throws Exception {
+ PathResource resource = new PathResource(TEST_DIR);
+ assertThat(resource.isWritable(), equalTo(false));
+ }
+
+ @Test
+ public void outputStream() throws Exception {
+ PathResource resource = new PathResource(temporaryFolder.newFile("test").toPath());
+ FileCopyUtils.copy("test".getBytes(), resource.getOutputStream());
+ assertThat(resource.contentLength(), equalTo(4L));
+ }
+
+ @Test
+ public void doesNotExistOutputStream() throws Exception {
+ File file = temporaryFolder.newFile("test");
+ file.delete();
+ PathResource resource = new PathResource(file.toPath());
+ FileCopyUtils.copy("test".getBytes(), resource.getOutputStream());
+ assertThat(resource.contentLength(), equalTo(4L));
+ }
+
+ @Test
+ public void directoryOutputStream() throws Exception {
+ PathResource resource = new PathResource(TEST_DIR);
+ thrown.expect(FileNotFoundException.class);
+ resource.getOutputStream();
+ }
+
+}
diff --git a/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java b/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java
new file mode 100644
index 0000000000..e69767c491
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.util;
+
+import java.util.SortedSet;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.springframework.util.SocketUtils.*;
+
+/**
+ * Unit tests for {@link SocketUtils}.
+ *
+ * @author Sam Brannen
+ * @since 4.0
+ */
+public class SocketUtilsTests {
+
+ private void assertPortInRange(int port, int minPort, int maxPort) {
+ assertTrue("port [" + port + "] >= " + minPort, port >= minPort);
+ assertTrue("port [" + port + "] <= " + maxPort, port <= maxPort);
+ }
+
+ private void assertAvailablePorts(SortedSet ports, int numRequested, int minPort, int maxPort) {
+ assertEquals("number of ports requested", numRequested, ports.size());
+ for (int port : ports) {
+ assertPortInRange(port, minPort, maxPort);
+ }
+ }
+
+ // --- TCP -----------------------------------------------------------------
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findAvailableTcpPortWithZeroMinPort() {
+ SocketUtils.findAvailableTcpPort(0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findAvailableTcpPortWithNegativeMinPort() {
+ SocketUtils.findAvailableTcpPort(-500);
+ }
+
+ @Test
+ public void findAvailableTcpPort() {
+ int port = SocketUtils.findAvailableTcpPort();
+ assertPortInRange(port, PORT_RANGE_MIN, PORT_RANGE_MAX);
+ }
+
+ @Test
+ public void findAvailableTcpPortWithMin() {
+ int port = SocketUtils.findAvailableTcpPort(50000);
+ assertPortInRange(port, 50000, PORT_RANGE_MAX);
+ }
+
+ @Test
+ public void findAvailableTcpPortInRange() {
+ int minPort = 20000;
+ int maxPort = minPort + 1000;
+ int port = SocketUtils.findAvailableTcpPort(minPort, maxPort);
+ assertPortInRange(port, minPort, maxPort);
+ }
+
+ @Test
+ public void find4AvailableTcpPorts() {
+ findAvailableTcpPorts(4);
+ }
+
+ @Test
+ public void find50AvailableTcpPorts() {
+ findAvailableTcpPorts(50);
+ }
+
+ @Test
+ public void find4AvailableTcpPortsInRange() {
+ findAvailableTcpPorts(4, 30000, 35000);
+ }
+
+ @Test
+ public void find50AvailableTcpPortsInRange() {
+ findAvailableTcpPorts(50, 40000, 45000);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findAvailableTcpPortsWithRequestedNumberGreaterThanSizeOfRange() {
+ findAvailableTcpPorts(50, 45000, 45010);
+ }
+
+ private void findAvailableTcpPorts(int numRequested) {
+ SortedSet ports = SocketUtils.findAvailableTcpPorts(numRequested);
+ assertAvailablePorts(ports, numRequested, PORT_RANGE_MIN, PORT_RANGE_MAX);
+ }
+
+ private void findAvailableTcpPorts(int numRequested, int minPort, int maxPort) {
+ SortedSet ports = SocketUtils.findAvailableTcpPorts(numRequested, minPort, maxPort);
+ assertAvailablePorts(ports, numRequested, minPort, maxPort);
+ }
+
+ // --- UDP -----------------------------------------------------------------
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findAvailableUdpPortWithZeroMinPort() {
+ SocketUtils.findAvailableUdpPort(0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findAvailableUdpPortWithNegativeMinPort() {
+ SocketUtils.findAvailableUdpPort(-500);
+ }
+
+ @Test
+ public void findAvailableUdpPort() {
+ int port = SocketUtils.findAvailableUdpPort();
+ assertPortInRange(port, PORT_RANGE_MIN, PORT_RANGE_MAX);
+ }
+
+ @Test
+ public void findAvailableUdpPortWithMin() {
+ int port = SocketUtils.findAvailableUdpPort(50000);
+ assertPortInRange(port, 50000, PORT_RANGE_MAX);
+ }
+
+ @Test
+ public void findAvailableUdpPortInRange() {
+ int minPort = 20000;
+ int maxPort = minPort + 1000;
+ int port = SocketUtils.findAvailableUdpPort(minPort, maxPort);
+ assertPortInRange(port, minPort, maxPort);
+ }
+
+ @Test
+ public void find4AvailableUdpPorts() {
+ findAvailableUdpPorts(4);
+ }
+
+ @Test
+ public void find50AvailableUdpPorts() {
+ findAvailableUdpPorts(50);
+ }
+
+ @Test
+ public void find4AvailableUdpPortsInRange() {
+ findAvailableUdpPorts(4, 30000, 35000);
+ }
+
+ @Test
+ public void find50AvailableUdpPortsInRange() {
+ findAvailableUdpPorts(50, 40000, 45000);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void findAvailableUdpPortsWithRequestedNumberGreaterThanSizeOfRange() {
+ findAvailableUdpPorts(50, 45000, 45010);
+ }
+
+ private void findAvailableUdpPorts(int numRequested) {
+ SortedSet ports = SocketUtils.findAvailableUdpPorts(numRequested);
+ assertAvailablePorts(ports, numRequested, PORT_RANGE_MIN, PORT_RANGE_MAX);
+ }
+
+ private void findAvailableUdpPorts(int numRequested, int minPort, int maxPort) {
+ SortedSet ports = SocketUtils.findAvailableUdpPorts(numRequested, minPort, maxPort);
+ assertAvailablePorts(ports, numRequested, minPort, maxPort);
+ }
+
+}
diff --git a/spring-expression/src/main/java/org/springframework/expression/ConstructorExecutor.java b/spring-expression/src/main/java/org/springframework/expression/ConstructorExecutor.java
index bb04735237..71efb3039a 100644
--- a/spring-expression/src/main/java/org/springframework/expression/ConstructorExecutor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/ConstructorExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,13 +19,15 @@ package org.springframework.expression;
// TODO Is the resolver/executor model too pervasive in this package?
/**
- * Executors are built by resolvers and can be cached by the infrastructure to repeat an operation quickly without going
- * back to the resolvers. For example, the particular constructor to run on a class may be discovered by the reflection
- * constructor resolver - it will then build a ConstructorExecutor that executes that constructor and the
- * ConstructorExecutor can be reused without needing to go back to the resolver to discover the constructor again.
+ * Executors are built by resolvers and can be cached by the infrastructure to repeat an
+ * operation quickly without going back to the resolvers. For example, the particular
+ * constructor to run on a class may be discovered by the reflection constructor resolver
+ * - it will then build a ConstructorExecutor that executes that constructor and the
+ * ConstructorExecutor can be reused without needing to go back to the resolver to
+ * discover the constructor again.
*
- * They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go
- * back to the resolvers to ask for a new one.
+ * They can become stale, and in that case should throw an AccessException - this will
+ * cause the infrastructure to go back to the resolvers to ask for a new one.
*
* @author Andy Clement
* @since 3.0
@@ -34,11 +36,13 @@ public interface ConstructorExecutor {
/**
* Execute a constructor in the specified context using the specified arguments.
+ *
* @param context the evaluation context in which the command is being executed
- * @param arguments the arguments to the constructor call, should match (in terms of number and type) whatever the
- * command will need to run
+ * @param arguments the arguments to the constructor call, should match (in terms of
+ * number and type) whatever the command will need to run
* @return the new object
- * @throws AccessException if there is a problem executing the command or the CommandExecutor is no longer valid
+ * @throws AccessException if there is a problem executing the command or the
+ * CommandExecutor is no longer valid
*/
TypedValue execute(EvaluationContext context, Object... arguments) throws AccessException;
diff --git a/spring-expression/src/main/java/org/springframework/expression/ConstructorResolver.java b/spring-expression/src/main/java/org/springframework/expression/ConstructorResolver.java
index e1892d86c0..d942978af9 100644
--- a/spring-expression/src/main/java/org/springframework/expression/ConstructorResolver.java
+++ b/spring-expression/src/main/java/org/springframework/expression/ConstructorResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,18 +21,19 @@ import java.util.List;
import org.springframework.core.convert.TypeDescriptor;
/**
- * A constructor resolver attempts locate a constructor and returns a ConstructorExecutor that can be used to invoke
- * that constructor. The ConstructorExecutor will be cached but if it 'goes stale' the resolvers will be called again.
- *
+ * A constructor resolver attempts locate a constructor and returns a ConstructorExecutor
+ * that can be used to invoke that constructor. The ConstructorExecutor will be cached but
+ * if it 'goes stale' the resolvers will be called again.
+ *
* @author Andy Clement
* @since 3.0
*/
public interface ConstructorResolver {
/**
- * Within the supplied context determine a suitable constructor on the supplied type that can handle the
- * specified arguments. Return a ConstructorExecutor that can be used to invoke that constructor
- * (or {@code null} if no constructor could be found).
+ * Within the supplied context determine a suitable constructor on the supplied type
+ * that can handle the specified arguments. Return a ConstructorExecutor that can be
+ * used to invoke that constructor (or {@code null} if no constructor could be found).
* @param context the current evaluation context
* @param typeName the type upon which to look for the constructor
* @param argumentTypes the arguments that the constructor must be able to handle
diff --git a/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
index 27a83cab37..a4953fe06d 100644
--- a/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
+++ b/spring-expression/src/main/java/org/springframework/expression/EvaluationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,12 @@ package org.springframework.expression;
import java.util.List;
/**
- * Expressions are executed in an evaluation context. It is in this context that references
- * are resolved when encountered during expression evaluation.
+ * Expressions are executed in an evaluation context. It is in this context that
+ * references are resolved when encountered during expression evaluation.
*
*
There is a default implementation of the EvaluationContext,
- * {@link org.springframework.expression.spel.support.StandardEvaluationContext}
- * that can be extended, rather than having to implement everything.
+ * {@link org.springframework.expression.spel.support.StandardEvaluationContext} that can
+ * be extended, rather than having to implement everything.
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -33,8 +33,9 @@ import java.util.List;
public interface EvaluationContext {
/**
- * @return the default root context object against which unqualified properties/methods/etc
- * should be resolved. This can be overridden when evaluating an expression.
+ * @return the default root context object against which unqualified
+ * properties/methods/etc should be resolved. This can be overridden when
+ * evaluating an expression.
*/
TypedValue getRootObject();
@@ -54,7 +55,8 @@ public interface EvaluationContext {
List getPropertyAccessors();
/**
- * @return a type locator that can be used to find types, either by short or fully qualified name.
+ * @return a type locator that can be used to find types, either by short or fully
+ * qualified name.
*/
TypeLocator getTypeLocator();
diff --git a/spring-expression/src/main/java/org/springframework/expression/Expression.java b/spring-expression/src/main/java/org/springframework/expression/Expression.java
index fdad75b17f..b72a353d70 100644
--- a/spring-expression/src/main/java/org/springframework/expression/Expression.java
+++ b/spring-expression/src/main/java/org/springframework/expression/Expression.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,10 +19,9 @@ package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
- * An expression capable of evaluating itself against context objects.
- * Encapsulates the details of a previously parsed expression string.
- * Provides a common abstraction for expression evaluation independent
- * of any language like OGNL or the Unified EL.
+ * An expression capable of evaluating itself against context objects. Encapsulates the
+ * details of a previously parsed expression string. Provides a common abstraction for
+ * expression evaluation independent of any language like OGNL or the Unified EL.
*
* @author Keith Donald
* @author Andy Clement
diff --git a/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java b/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java
index ecea713cad..240c8053be 100644
--- a/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java
+++ b/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
package org.springframework.expression;
-
/**
* Super class for exceptions that can occur whilst processing expressions
*
@@ -27,8 +26,10 @@ package org.springframework.expression;
public class ExpressionException extends RuntimeException {
protected String expressionString;
+
protected int position; // -1 if not known - but should be known in all reasonable cases
+
/**
* Creates a new expression exception.
* @param expressionString the expression string
@@ -85,15 +86,16 @@ public class ExpressionException extends RuntimeException {
super(message,cause);
}
+
public String toDetailedString() {
StringBuilder output = new StringBuilder();
- if (expressionString!=null) {
+ if (this.expressionString!=null) {
output.append("Expression '");
- output.append(expressionString);
+ output.append(this.expressionString);
output.append("'");
- if (position!=-1) {
+ if (this.position!=-1) {
output.append(" @ ");
- output.append(position);
+ output.append(this.position);
}
output.append(": ");
}
@@ -106,7 +108,7 @@ public class ExpressionException extends RuntimeException {
}
public final int getPosition() {
- return position;
+ return this.position;
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/ExpressionInvocationTargetException.java b/spring-expression/src/main/java/org/springframework/expression/ExpressionInvocationTargetException.java
index a6b11fb26a..30a4e00843 100644
--- a/spring-expression/src/main/java/org/springframework/expression/ExpressionInvocationTargetException.java
+++ b/spring-expression/src/main/java/org/springframework/expression/ExpressionInvocationTargetException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.expression;
/**
- * This exception wraps (as cause) a checked exception thrown by some method that SpEL invokes.
- * It differs from a SpelEvaluationException because this indicates the occurrence of a checked exception
- * that the invoked method was defined to throw. SpelEvaluationExceptions are for handling (and wrapping)
- * unexpected exceptions.
+ * This exception wraps (as cause) a checked exception thrown by some method that SpEL
+ * invokes. It differs from a SpelEvaluationException because this indicates the
+ * occurrence of a checked exception that the invoked method was defined to throw.
+ * SpelEvaluationExceptions are for handling (and wrapping) unexpected exceptions.
*
* @author Andy Clement
* @since 3.0.3
diff --git a/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java b/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java
index bd4dd74516..1506f98630 100644
--- a/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/MethodExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,15 @@
package org.springframework.expression;
/**
- * MethodExecutors are built by the resolvers and can be cached by the infrastructure to repeat an operation quickly
- * without going back to the resolvers. For example, the particular method to run on an object may be discovered by the
- * reflection method resolver - it will then build a MethodExecutor that executes that method and the MethodExecutor can
- * be reused without needing to go back to the resolver to discover the method again.
+ * MethodExecutors are built by the resolvers and can be cached by the infrastructure to
+ * repeat an operation quickly without going back to the resolvers. For example, the
+ * particular method to run on an object may be discovered by the reflection method
+ * resolver - it will then build a MethodExecutor that executes that method and the
+ * MethodExecutor can be reused without needing to go back to the resolver to discover the
+ * method again.
*
- * 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.
+ *
They can become stale, and in that case should throw an AccessException - this will
+ * cause the infrastructure to go back to the resolvers to ask for a new one.
*
* @author Andy Clement
* @since 3.0
@@ -31,13 +33,15 @@ package org.springframework.expression;
public interface MethodExecutor {
/**
- * Execute a command using the specified arguments, and using the specified expression state.
+ * Execute a command using the specified arguments, and using the specified expression
+ * state.
* @param context the evaluation context in which the command is being executed
* @param target the target object of the call - null for static methods
- * @param arguments the arguments to the executor, should match (in terms of number and type) whatever the
- * command will need to run
+ * @param arguments the arguments to the executor, should match (in terms of number
+ * and type) whatever the command will need to run
* @return the value returned from execution
- * @throws AccessException if there is a problem executing the command or the MethodExecutor is no longer valid
+ * @throws AccessException if there is a problem executing the command or the
+ * MethodExecutor is no longer valid
*/
TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException;
diff --git a/spring-expression/src/main/java/org/springframework/expression/MethodFilter.java b/spring-expression/src/main/java/org/springframework/expression/MethodFilter.java
index 50992dd798..d7c6e20cbd 100644
--- a/spring-expression/src/main/java/org/springframework/expression/MethodFilter.java
+++ b/spring-expression/src/main/java/org/springframework/expression/MethodFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,18 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.expression;
import java.lang.reflect.Method;
import java.util.List;
/**
- * MethodFilter instances allow SpEL users to fine tune the behaviour of the method resolution
- * process. Method resolution (which translates from a method name in an expression to a real
- * method to invoke) will normally retrieve candidate methods for invocation via a simple call
- * to 'Class.getMethods()' and will choose the first one that is suitable for the
- * input parameters. By registering a MethodFilter the user can receive a callback
- * and change the methods that will be considered suitable.
+ * MethodFilter instances allow SpEL users to fine tune the behaviour of the method
+ * resolution process. Method resolution (which translates from a method name in an
+ * expression to a real method to invoke) will normally retrieve candidate methods for
+ * invocation via a simple call to 'Class.getMethods()' and will choose the first one that
+ * is suitable for the input parameters. By registering a MethodFilter the user can
+ * receive a callback and change the methods that will be considered suitable.
*
* @author Andy Clement
* @since 3.0.1
@@ -32,12 +33,11 @@ import java.util.List;
public interface MethodFilter {
/**
- * Called by the method resolver to allow the SpEL user to organize the list of candidate
- * methods that may be invoked. The filter can remove methods that should not be
- * considered candidates and it may sort the results. The resolver will then search
- * through the methods as returned from the filter when looking for a suitable
+ * Called by the method resolver to allow the SpEL user to organize the list of
+ * candidate methods that may be invoked. The filter can remove methods that should
+ * not be considered candidates and it may sort the results. The resolver will then
+ * search through the methods as returned from the filter when looking for a suitable
* candidate to invoke.
- *
* @param methods the full list of methods the resolver was going to choose from
* @return a possible subset of input methods that may be sorted by order of relevance
*/
diff --git a/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java b/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java
index 75cdc5eab8..d3bdb78c96 100644
--- a/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java
+++ b/spring-expression/src/main/java/org/springframework/expression/MethodResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,9 @@ import java.util.List;
import org.springframework.core.convert.TypeDescriptor;
/**
- * A method resolver attempts locate a method and returns a command executor that can be used to invoke that method.
- * The command executor will be cached but if it 'goes stale' the resolvers will be called again.
+ * A method resolver attempts locate a method and returns a command executor that can be
+ * used to invoke that method. The command executor will be cached but if it 'goes stale'
+ * the resolvers will be called again.
*
* @author Andy Clement
* @since 3.0
@@ -30,13 +31,14 @@ import org.springframework.core.convert.TypeDescriptor;
public interface MethodResolver {
/**
- * Within the supplied context determine a suitable method on the supplied object that can handle the
- * specified arguments. Return a MethodExecutor that can be used to invoke that method
- * (or {@code null} if no method could be found).
+ * Within the supplied context determine a suitable method on the supplied object that
+ * can handle the specified arguments. Return a MethodExecutor that can be used to
+ * invoke that method (or {@code null} if no method could be found).
* @param context the current evaluation context
* @param targetObject the object upon which the method is being called
* @param argumentTypes the arguments that the constructor must be able to handle
- * @return a MethodExecutor that can invoke the method, or null if the method cannot be found
+ * @return a MethodExecutor that can invoke the method, or null if the method cannot
+ * be found
*/
MethodExecutor resolve(EvaluationContext context, Object targetObject, String name,
List argumentTypes) throws AccessException;
diff --git a/spring-expression/src/main/java/org/springframework/expression/Operation.java b/spring-expression/src/main/java/org/springframework/expression/Operation.java
index 01b805905b..f4b9673704 100644
--- a/spring-expression/src/main/java/org/springframework/expression/Operation.java
+++ b/spring-expression/src/main/java/org/springframework/expression/Operation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,24 @@
package org.springframework.expression;
/**
- * Supported operations that an {@link OperatorOverloader} can implement for any pair of operands.
+ * Supported operations that an {@link OperatorOverloader} can implement for any pair of
+ * operands.
*
* @author Andy Clement
* @since 3.0
*/
public enum Operation {
- ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS, POWER
+ ADD,
+
+ SUBTRACT,
+
+ DIVIDE,
+
+ MULTIPLY,
+
+ MODULUS,
+
+ POWER
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/OperatorOverloader.java b/spring-expression/src/main/java/org/springframework/expression/OperatorOverloader.java
index 29c165111b..7291575c9c 100644
--- a/spring-expression/src/main/java/org/springframework/expression/OperatorOverloader.java
+++ b/spring-expression/src/main/java/org/springframework/expression/OperatorOverloader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,9 @@
package org.springframework.expression;
/**
- * By default the mathematical operators {@link Operation} support simple types like numbers. By providing an
- * implementation of OperatorOverloader, a user of the expression language can support these operations on other types.
+ * By default the mathematical operators {@link Operation} support simple types like
+ * numbers. By providing an implementation of OperatorOverloader, a user of the expression
+ * language can support these operations on other types.
*
* @author Andy Clement
* @since 3.0
@@ -26,20 +27,21 @@ package org.springframework.expression;
public interface OperatorOverloader {
/**
- * Return true if the operator overloader supports the specified operation
- * between the two operands and so should be invoked to handle it.
+ * Return true if the operator overloader supports the specified operation between the
+ * two operands and so should be invoked to handle it.
* @param operation the operation to be performed
* @param leftOperand the left operand
* @param rightOperand the right operand
- * @return true if the OperatorOverloader supports the specified operation between the two operands
+ * @return true if the OperatorOverloader supports the specified operation between the
+ * two operands
* @throws EvaluationException if there is a problem performing the operation
*/
boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand)
throws EvaluationException;
/**
- * Execute the specified operation on two operands, returning a result.
- * See {@link Operation} for supported operations.
+ * Execute the specified operation on two operands, returning a result. See
+ * {@link Operation} for supported operations.
* @param operation the operation to be performed
* @param leftOperand the left operand
* @param rightOperand the right operand
diff --git a/spring-expression/src/main/java/org/springframework/expression/ParserContext.java b/spring-expression/src/main/java/org/springframework/expression/ParserContext.java
index 87a442c6b3..9617feb247 100644
--- a/spring-expression/src/main/java/org/springframework/expression/ParserContext.java
+++ b/spring-expression/src/main/java/org/springframework/expression/ParserContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,8 @@
package org.springframework.expression;
/**
- * Input provided to an expression parser that can influence an expression parsing/compilation routine.
+ * Input provided to an expression parser that can influence an expression
+ * parsing/compilation routine.
*
* @author Keith Donald
* @author Andy Clement
@@ -26,38 +27,35 @@ package org.springframework.expression;
public interface ParserContext {
/**
- * Whether or not the expression being parsed is a template. A template expression consists of literal text that can
- * be mixed with evaluatable blocks. Some examples:
- *
+ * Whether or not the expression being parsed is a template. A template expression
+ * consists of literal text that can be mixed with evaluatable blocks. Some examples:
*
* Some literal text
* Hello #{name.firstName}!
* #{3 + 4}
*
- *
* @return true if the expression is a template, false otherwise
*/
boolean isTemplate();
/**
- * For template expressions, returns the prefix that identifies the start of an expression block within a string.
- * For example: "${"
- *
+ * For template expressions, returns the prefix that identifies the start of an
+ * expression block within a string. For example: "${"
* @return the prefix that identifies the start of an expression
*/
String getExpressionPrefix();
/**
- * For template expressions, return the prefix that identifies the end of an expression block within a string.
- * For example: "}"
- *
+ * For template expressions, return the prefix that identifies the end of an
+ * expression block within a string. For example: "}"
* @return the suffix that identifies the end of an expression
*/
String getExpressionSuffix();
+
/**
- * The default ParserContext implementation that enables template expression parsing mode.
- * The expression prefix is #{ and the expression suffix is }.
+ * The default ParserContext implementation that enables template expression parsing
+ * mode. The expression prefix is #{ and the expression suffix is }.
* @see #isTemplate()
*/
public static final ParserContext TEMPLATE_EXPRESSION = new ParserContext() {
diff --git a/spring-expression/src/main/java/org/springframework/expression/PropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/PropertyAccessor.java
index e14c30ba68..a8222c25ab 100644
--- a/spring-expression/src/main/java/org/springframework/expression/PropertyAccessor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/PropertyAccessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,15 @@ package org.springframework.expression;
/**
- * A property accessor is able to read (and possibly write) to object properties. The interface places no restrictions
- * and so implementors are free to access properties directly as fields or through getters or in any other way they see
- * as appropriate. A resolver can optionally specify an array of target classes for which it should be called - but if
- * it returns null from getSpecificTargetClasses() then it will be called for all property references and given a chance
- * to determine if it can read or write them. Property resolvers are considered to be ordered and each will be called in
- * turn. The only rule that affects the call order is that any naming the target class directly in
- * getSpecifiedTargetClasses() will be called first, before the general resolvers.
+ * A property accessor is able to read (and possibly write) to object properties. The
+ * interface places no restrictions and so implementors are free to access properties
+ * directly as fields or through getters or in any other way they see as appropriate. A
+ * resolver can optionally specify an array of target classes for which it should be
+ * called - but if it returns null from getSpecificTargetClasses() then it will be called
+ * for all property references and given a chance to determine if it can read or write
+ * them. Property resolvers are considered to be ordered and each will be called in turn.
+ * The only rule that affects the call order is that any naming the target class directly
+ * in getSpecifiedTargetClasses() will be called first, before the general resolvers.
*
* @author Andy Clement
* @since 3.0
@@ -32,19 +34,23 @@ package org.springframework.expression;
public interface PropertyAccessor {
/**
- * Return an array of classes for which this resolver should be called. Returning null indicates this is a general
- * resolver that can be called in an attempt to resolve a property on any type.
- * @return an array of classes that this resolver is suitable for (or null if a general resolver)
+ * Return an array of classes for which this resolver should be called. Returning null
+ * indicates this is a general resolver that can be called in an attempt to resolve a
+ * property on any type.
+ * @return an array of classes that this resolver is suitable for (or null if a
+ * general resolver)
*/
Class[] getSpecificTargetClasses();
/**
- * Called to determine if a resolver instance is able to access a specified property on a specified target object.
+ * Called to determine if a resolver instance is able to access a specified property
+ * on a specified target object.
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @return true if this resolver is able to read the property
- * @throws AccessException if there is any problem determining whether the property can be read
+ * @throws AccessException if there is any problem determining whether the property
+ * can be read
*/
boolean canRead(EvaluationContext context, Object target, String name) throws AccessException;
@@ -59,17 +65,20 @@ public interface PropertyAccessor {
TypedValue read(EvaluationContext context, Object target, String name) throws AccessException;
/**
- * Called to determine if a resolver instance is able to write to a specified property on a specified target object.
+ * Called to determine if a resolver instance is able to write to a specified property
+ * on a specified target object.
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
* @return true if this resolver is able to write to the property
- * @throws AccessException if there is any problem determining whether the property can be written to
+ * @throws AccessException if there is any problem determining whether the property
+ * can be written to
*/
boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException;
/**
- * Called to write to a property on a specified target object. Should only succeed if canWrite() also returns true.
+ * Called to write to a property on a specified target object. Should only succeed if
+ * canWrite() also returns true.
* @param context the evaluation context in which the access is being attempted
* @param target the target object upon which the property is being accessed
* @param name the name of the property being accessed
diff --git a/spring-expression/src/main/java/org/springframework/expression/TypeComparator.java b/spring-expression/src/main/java/org/springframework/expression/TypeComparator.java
index 1dfb41adc7..c939fcab11 100644
--- a/spring-expression/src/main/java/org/springframework/expression/TypeComparator.java
+++ b/spring-expression/src/main/java/org/springframework/expression/TypeComparator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,8 +17,8 @@
package org.springframework.expression;
/**
- * Instances of a type comparator should be able to compare pairs of objects for equality, the specification of the
- * return value is the same as for {@link Comparable}.
+ * Instances of a type comparator should be able to compare pairs of objects for equality,
+ * the specification of the return value is the same as for {@link Comparable}.
*
* @author Andy Clement
* @since 3.0
@@ -29,9 +29,10 @@ public interface TypeComparator {
* Compare two objects.
* @param firstObject the first object
* @param secondObject the second object
- * @return 0 if they are equal, <0 if the first is smaller than the second, or >0 if the first is larger than the
- * second
- * @throws EvaluationException if a problem occurs during comparison (or they are not comparable)
+ * @return 0 if they are equal, <0 if the first is smaller than the second, or >0 if
+ * the first is larger than the second
+ * @throws EvaluationException if a problem occurs during comparison (or they are not
+ * comparable)
*/
int compare(Object firstObject, Object secondObject) throws EvaluationException;
diff --git a/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java b/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java
index e6145ab20e..bb7b26b386 100644
--- a/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java
+++ b/spring-expression/src/main/java/org/springframework/expression/TypeConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,10 +19,10 @@ package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
- * A type converter can convert values between different types encountered
- * during expression evaluation. This is an SPI for the expression parser;
- * see {@link org.springframework.core.convert.ConversionService} for the
- * primary user API to Spring's conversion facilities.
+ * A type converter can convert values between different types encountered during
+ * expression evaluation. This is an SPI for the expression parser; see
+ * {@link org.springframework.core.convert.ConversionService} for the primary user API to
+ * Spring's conversion facilities.
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -31,7 +31,8 @@ import org.springframework.core.convert.TypeDescriptor;
public interface TypeConverter {
/**
- * Return true if the type converter can convert the specified type to the desired target type.
+ * Return true if the type converter can convert the specified type to the desired
+ * target type.
* @param sourceType a type descriptor that describes the source type
* @param targetType a type descriptor that describes the requested result type
* @return true if that conversion can be performed
@@ -39,12 +40,15 @@ public interface TypeConverter {
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
/**
- * Convert (may coerce) a value from one type to another, for example from a boolean to a string.
- * The typeDescriptor parameter enables support for typed collections - if the caller really wishes they
- * can have a List<Integer> for example, rather than simply a List.
+ * Convert (may coerce) a value from one type to another, for example from a boolean
+ * to a string. The typeDescriptor parameter enables support for typed collections -
+ * if the caller really wishes they can have a List<Integer> for example, rather
+ * than simply a List.
* @param value the value to be converted
- * @param sourceType a type descriptor that supplies extra information about the source object
- * @param targetType a type descriptor that supplies extra information about the requested result type
+ * @param sourceType a type descriptor that supplies extra information about the
+ * source object
+ * @param targetType a type descriptor that supplies extra information about the
+ * requested result type
* @return the converted value
* @throws EvaluationException if conversion is not possible
*/
diff --git a/spring-expression/src/main/java/org/springframework/expression/TypeLocator.java b/spring-expression/src/main/java/org/springframework/expression/TypeLocator.java
index 4a22a82c19..7fade5cb7b 100644
--- a/spring-expression/src/main/java/org/springframework/expression/TypeLocator.java
+++ b/spring-expression/src/main/java/org/springframework/expression/TypeLocator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,11 @@
package org.springframework.expression;
/**
- * Implementors of this interface are expected to be able to locate types. They may use custom classloaders
- * or the and deal with common package prefixes (java.lang, etc) however they wish. See
- * {@link org.springframework.expression.spel.support.StandardTypeLocator} for an example implementation.
+ * Implementors of this interface are expected to be able to locate types. They may use
+ * custom classloaders or the and deal with common package prefixes (java.lang, etc)
+ * however they wish. See
+ * {@link org.springframework.expression.spel.support.StandardTypeLocator} for an example
+ * implementation.
*
* @author Andy Clement
* @since 3.0
@@ -27,7 +29,8 @@ package org.springframework.expression;
public interface TypeLocator {
/**
- * Find a type by name. The name may or may not be fully qualified (eg. String or java.lang.String)
+ * Find a type by name. The name may or may not be fully qualified (eg. String or
+ * java.lang.String)
* @param typename the type to be located
* @return the class object representing that type
* @throws EvaluationException if there is a problem finding it
diff --git a/spring-expression/src/main/java/org/springframework/expression/TypedValue.java b/spring-expression/src/main/java/org/springframework/expression/TypedValue.java
index 67ecb99ad2..12fb458293 100644
--- a/spring-expression/src/main/java/org/springframework/expression/TypedValue.java
+++ b/spring-expression/src/main/java/org/springframework/expression/TypedValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,9 +19,9 @@ package org.springframework.expression;
import org.springframework.core.convert.TypeDescriptor;
/**
- * Encapsulates an object and a type descriptor that describes it.
- * The type descriptor can hold generic information that would not be
- * accessible through a simple {@code getClass()} call on the object.
+ * Encapsulates an object and a type descriptor that describes it. The type descriptor can
+ * hold generic information that would not be accessible through a simple
+ * {@code getClass()} call on the object.
*
* @author Andy Clement
* @author Juergen Hoeller
diff --git a/spring-expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java b/spring-expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java
index a24a545479..1ddd5f9143 100644
--- a/spring-expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java
+++ b/spring-expression/src/main/java/org/springframework/expression/common/CompositeStringExpression.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,18 +20,21 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
+import org.springframework.expression.TypedValue;
/**
- * Represents a template expression broken into pieces. Each piece will be an Expression but pure text parts to the
- * template will be represented as LiteralExpression objects. An example of a template expression might be:
- *
+ * Represents a template expression broken into pieces. Each piece will be an Expression
+ * but pure text parts to the template will be represented as LiteralExpression objects.
+ * An example of a template expression might be:
+ *
*
- * "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 {@code getName()} when invoked.
- *
+ * "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 {@code getName()} when invoked.
+ *
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
@@ -131,13 +134,13 @@ public class CompositeStringExpression implements Expression {
@Override
public T getValue(EvaluationContext context, Class expectedResultType) throws EvaluationException {
Object value = getValue(context);
- return ExpressionUtils.convert(context, value, expectedResultType);
+ return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
}
@Override
public T getValue(Class expectedResultType) throws EvaluationException {
Object value = getValue();
- return ExpressionUtils.convert(null, value, expectedResultType);
+ return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
}
@Override
@@ -146,21 +149,21 @@ public class CompositeStringExpression implements Expression {
}
public Expression[] getExpressions() {
- return expressions;
+ return this.expressions;
}
@Override
public T getValue(Object rootObject, Class desiredResultType) throws EvaluationException {
Object value = getValue(rootObject);
- return ExpressionUtils.convert(null, value, desiredResultType);
+ return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
}
@Override
public T getValue(EvaluationContext context, Object rootObject, Class desiredResultType)
throws EvaluationException {
Object value = getValue(context,rootObject);
- return ExpressionUtils.convert(context, value, desiredResultType);
+ return ExpressionUtils.convertTypedValue(context, new TypedValue(value), desiredResultType);
}
@Override
diff --git a/spring-expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java b/spring-expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java
index cc959c799e..696d1eedf9 100644
--- a/spring-expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java
+++ b/spring-expression/src/main/java/org/springframework/expression/common/ExpressionUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,29 +33,32 @@ import org.springframework.util.ClassUtils;
public abstract class ExpressionUtils {
/**
- * Determines if there is a type converter available in the specified context and attempts to use it to convert the
- * supplied value to the specified type. Throws an exception if conversion is not possible.
+ * Determines if there is a type converter available in the specified context and
+ * attempts to use it to convert the supplied value to the specified type. Throws an
+ * exception if conversion is not possible.
* @param context the evaluation context that may define a type converter
* @param value the value to convert (may be null)
* @param targetType the type to attempt conversion to
* @return the converted value
- * @throws EvaluationException if there is a problem during conversion or conversion of the value to the specified
- * type is not supported
+ * @throws EvaluationException if there is a problem during conversion or conversion
+ * of the value to the specified type is not supported
+ * @deprecated use {@link #convertTypedValue(EvaluationContext, TypedValue, Class)}
*/
+ @Deprecated
public static T convert(EvaluationContext context, Object value, Class targetType) throws EvaluationException {
- // TODO remove this function over time and use the one it delegates to
- return convertTypedValue(context,new TypedValue(value),targetType);
+ return convertTypedValue(context, new TypedValue(value), targetType);
}
/**
- * Determines if there is a type converter available in the specified context and attempts to use it to convert the
- * supplied value to the specified type. Throws an exception if conversion is not possible.
+ * Determines if there is a type converter available in the specified context and
+ * attempts to use it to convert the supplied value to the specified type. Throws an
+ * exception if conversion is not possible.
* @param context the evaluation context that may define a type converter
* @param typedValue the value to convert and a type descriptor describing it
* @param targetType the type to attempt conversion to
* @return the converted value
- * @throws EvaluationException if there is a problem during conversion or conversion of the value to the specified
- * type is not supported
+ * @throws EvaluationException if there is a problem during conversion or conversion
+ * of the value to the specified type is not supported
*/
@SuppressWarnings("unchecked")
public static T convertTypedValue(EvaluationContext context, TypedValue typedValue, Class targetType) {
diff --git a/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java b/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java
index ea6742ae07..c9b1878ff3 100644
--- a/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java
+++ b/spring-expression/src/main/java/org/springframework/expression/common/LiteralExpression.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,13 +20,14 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
+import org.springframework.expression.TypedValue;
/**
- * A very simple hardcoded implementation of the Expression interface that represents a string literal.
- * It is used with CompositeStringExpression when representing a template expression which is made up
- * of pieces - some being real expressions to be handled by an EL implementation like Spel, and some
- * being just textual elements.
- *
+ * A very simple hardcoded implementation of the Expression interface that represents a
+ * string literal. It is used with CompositeStringExpression when representing a template
+ * expression which is made up of pieces - some being real expressions to be handled by an
+ * EL implementation like Spel, and some being just textual elements.
+ *
* @author Andy Clement
* @since 3.0
*/
@@ -78,19 +79,19 @@ public class LiteralExpression implements Expression {
@Override
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
- throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
+ throw new EvaluationException(this.literalValue, "Cannot call setValue() on a LiteralExpression");
}
@Override
public T getValue(EvaluationContext context, Class expectedResultType) throws EvaluationException {
Object value = getValue(context);
- return ExpressionUtils.convert(context, value, expectedResultType);
+ return ExpressionUtils.convertTypedValue(context, new TypedValue(value), expectedResultType);
}
@Override
public T getValue(Class expectedResultType) throws EvaluationException {
Object value = getValue();
- return ExpressionUtils.convert(null, value, expectedResultType);
+ return ExpressionUtils.convertTypedValue(null, new TypedValue(value), expectedResultType);
}
@Override
@@ -106,7 +107,7 @@ public class LiteralExpression implements Expression {
@Override
public T getValue(Object rootObject, Class desiredResultType) throws EvaluationException {
Object value = getValue(rootObject);
- return ExpressionUtils.convert(null, value, desiredResultType);
+ return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
}
@Override
@@ -117,7 +118,7 @@ public class LiteralExpression implements Expression {
@Override
public T getValue(EvaluationContext context, Object rootObject, Class desiredResultType) throws EvaluationException {
Object value = getValue(context, rootObject);
- return ExpressionUtils.convert(null, value, desiredResultType);
+ return ExpressionUtils.convertTypedValue(null, new TypedValue(value), desiredResultType);
}
@Override
@@ -147,7 +148,7 @@ public class LiteralExpression implements Expression {
@Override
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
- throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
+ throw new EvaluationException(this.literalValue, "Cannot call setValue() on a LiteralExpression");
}
@Override
@@ -157,7 +158,7 @@ public class LiteralExpression implements Expression {
@Override
public void setValue(Object rootObject, Object value) throws EvaluationException {
- throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
+ throw new EvaluationException(this.literalValue, "Cannot call setValue() on a LiteralExpression");
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java
index 82b6ab07bb..c74371e998 100644
--- a/spring-expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java
+++ b/spring-expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,9 @@ import org.springframework.expression.ParseException;
import org.springframework.expression.ParserContext;
/**
- * An expression parser that understands templates. It can be subclassed
- * by expression parsers that do not offer first class support for templating.
- *
+ * An expression parser that understands templates. It can be subclassed by expression
+ * parsers that do not offer first class support for templating.
+ *
* @author Keith Donald
* @author Juergen Hoeller
* @author Andy Clement
@@ -40,102 +40,124 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
* Default ParserContext instance for non-template expressions.
*/
private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() {
+
@Override
public String getExpressionPrefix() {
return null;
}
+
@Override
public String getExpressionSuffix() {
return null;
}
+
@Override
public boolean isTemplate() {
return false;
}
};
-
@Override
public Expression parseExpression(String expressionString) throws ParseException {
return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
}
@Override
- public Expression parseExpression(String expressionString, ParserContext context) throws ParseException {
+ public Expression parseExpression(String expressionString, ParserContext context)
+ throws ParseException {
if (context == null) {
context = NON_TEMPLATE_PARSER_CONTEXT;
}
+
if (context.isTemplate()) {
return parseTemplate(expressionString, context);
- } else {
+ }
+ else {
return doParseExpression(expressionString, context);
}
}
- private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException {
+ private Expression parseTemplate(String expressionString, ParserContext context)
+ throws ParseException {
if (expressionString.length() == 0) {
return new LiteralExpression("");
}
Expression[] expressions = parseExpressions(expressionString, context);
if (expressions.length == 1) {
return expressions[0];
- } else {
+ }
+ else {
return new CompositeStringExpression(expressionString, expressions);
}
}
-
/**
- * Helper that parses given expression string using the configured parser. The expression string can contain any
- * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static
- * pieces of text will also be returned as Expressions that just return that static piece of text. As a result,
- * evaluating all returned expressions and concatenating the results produces the complete evaluated string.
- * Unwrapping is only done of the outermost delimiters found, so the string 'hello ${foo${abc}}' would break into
- * the pieces 'hello ' and 'foo${abc}'. This means that expression languages that used ${..} as part of their
- * functionality are supported without any problem.
- * The parsing is aware of the structure of an embedded expression. It assumes that parentheses '(',
- * square brackets '[' and curly brackets '}' must be in pairs within the expression unless they are within a
- * string literal and a string literal starts and terminates with a single quote '.
- *
+ * Helper that parses given expression string using the configured parser. The
+ * expression string can contain any number of expressions all contained in "${...}"
+ * markers. For instance: "foo${expr0}bar${expr1}". The static pieces of text will
+ * also be returned as Expressions that just return that static piece of text. As a
+ * result, evaluating all returned expressions and concatenating the results produces
+ * the complete evaluated string. Unwrapping is only done of the outermost delimiters
+ * found, so the string 'hello ${foo${abc}}' would break into the pieces 'hello ' and
+ * 'foo${abc}'. This means that expression languages that used ${..} as part of their
+ * functionality are supported without any problem. The parsing is aware of the
+ * structure of an embedded expression. It assumes that parentheses '(', square
+ * brackets '[' and curly brackets '}' must be in pairs within the expression unless
+ * they are within a string literal and a string literal starts and terminates with a
+ * single quote '.
* @param expressionString the expression string
* @return the parsed expressions
* @throws ParseException when the expressions cannot be parsed
*/
- private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException {
+ private Expression[] parseExpressions(String expressionString, ParserContext context)
+ throws ParseException {
List expressions = new LinkedList();
String prefix = context.getExpressionPrefix();
String suffix = context.getExpressionSuffix();
int startIdx = 0;
while (startIdx < expressionString.length()) {
- int prefixIndex = expressionString.indexOf(prefix,startIdx);
+ int prefixIndex = expressionString.indexOf(prefix, startIdx);
if (prefixIndex >= startIdx) {
// an inner expression was found - this is a composite
if (prefixIndex > startIdx) {
- expressions.add(createLiteralExpression(context,expressionString.substring(startIdx, prefixIndex)));
+ expressions.add(createLiteralExpression(context,
+ expressionString.substring(startIdx, prefixIndex)));
}
int afterPrefixIndex = prefixIndex + prefix.length();
- int suffixIndex = skipToCorrectEndSuffix(prefix,suffix,expressionString,afterPrefixIndex);
+ int suffixIndex = skipToCorrectEndSuffix(prefix, suffix,
+ expressionString, afterPrefixIndex);
+
if (suffixIndex == -1) {
- throw new ParseException(expressionString, prefixIndex, "No ending suffix '" + suffix +
- "' for expression starting at character " + prefixIndex + ": " +
- expressionString.substring(prefixIndex));
+ throw new ParseException(expressionString, prefixIndex,
+ "No ending suffix '" + suffix
+ + "' for expression starting at character "
+ + prefixIndex + ": "
+ + expressionString.substring(prefixIndex));
}
+
if (suffixIndex == afterPrefixIndex) {
- throw new ParseException(expressionString, prefixIndex, "No expression defined within delimiter '" +
- prefix + suffix + "' at character " + prefixIndex);
- } else {
- String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
- expr = expr.trim();
- if (expr.length()==0) {
- throw new ParseException(expressionString, prefixIndex, "No expression defined within delimiter '" +
- prefix + suffix + "' at character " + prefixIndex);
- }
- expressions.add(doParseExpression(expr, context));
- startIdx = suffixIndex + suffix.length();
+ throw new ParseException(expressionString, prefixIndex,
+ "No expression defined within delimiter '" + prefix + suffix
+ + "' at character " + prefixIndex);
}
- } else {
+
+ String expr = expressionString.substring(prefixIndex + prefix.length(),
+ suffixIndex);
+ expr = expr.trim();
+
+ if (expr.length() == 0) {
+ throw new ParseException(expressionString, prefixIndex,
+ "No expression defined within delimiter '" + prefix + suffix
+ + "' at character " + prefixIndex);
+ }
+
+ expressions.add(doParseExpression(expr, context));
+ startIdx = suffixIndex + suffix.length();
+ }
+ else {
// no more ${expressions} found in string, add rest as static text
- expressions.add(createLiteralExpression(context,expressionString.substring(startIdx)));
+ expressions.add(createLiteralExpression(context,
+ expressionString.substring(startIdx)));
startIdx = expressionString.length();
}
}
@@ -147,19 +169,20 @@ public abstract class TemplateAwareExpressionParser implements ExpressionParser
}
/**
- * Return true if the specified suffix can be found at the supplied position in the supplied expression string.
+ * Return true if the specified suffix can be found at the supplied position in the
+ * supplied expression string.
* @param expressionString the expression string which may contain the suffix
* @param pos the start position at which to check for the suffix
* @param suffix the suffix string
*/
- private boolean isSuffixHere(String expressionString,int pos,String suffix) {
+ private boolean isSuffixHere(String expressionString, int pos, String suffix) {
int suffixPosition = 0;
- for (int i=0;i stack = new Stack();
- while (posIt also acts as a place for to define common utility routines that the various Ast nodes might need.
+ * It also acts as a place for to define common utility routines that the various AST
+ * nodes might need.
*
* @author Andy Clement
* @since 3.0
@@ -138,7 +141,8 @@ public class ExpressionState {
}
public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
- return this.relatedContext.getTypeConverter().convertValue(value, TypeDescriptor.forObject(value), targetTypeDescriptor);
+ return this.relatedContext.getTypeConverter().convertValue(value,
+ TypeDescriptor.forObject(value), targetTypeDescriptor);
}
public TypeConverter getTypeConverter() {
@@ -147,7 +151,8 @@ public class ExpressionState {
public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
Object val = value.getValue();
- return this.relatedContext.getTypeConverter().convertValue(val, TypeDescriptor.forObject(val), targetTypeDescriptor);
+ return this.relatedContext.getTypeConverter().convertValue(val,
+ TypeDescriptor.forObject(val), targetTypeDescriptor);
}
/*
@@ -210,6 +215,7 @@ public class ExpressionState {
return this.configuration;
}
+
/**
* A new scope is entered when a function is called and it is used to hold the parameters to the function call. If the names
* of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst
@@ -221,6 +227,7 @@ public class ExpressionState {
public VariableScope() { }
+
public VariableScope(Map arguments) {
if (arguments != null) {
this.vars.putAll(arguments);
@@ -231,6 +238,7 @@ public class ExpressionState {
this.vars.put(name,value);
}
+
public Object lookupVariable(String name) {
return this.vars.get(name);
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java
index 7c7a3f6f1c..80a604f690 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@ package org.springframework.expression.spel;
import org.springframework.expression.EvaluationException;
/**
- * Root exception for Spring EL related exceptions. Rather than holding a hard coded string indicating the problem, it
- * records a message key and the inserts for the message. See {@link SpelMessage} for the list of all possible messages
- * that can occur.
+ * Root exception for Spring EL related exceptions. Rather than holding a hard coded
+ * string indicating the problem, it records a message key and the inserts for the
+ * message. See {@link SpelMessage} for the list of all possible messages that can occur.
*
* @author Andy Clement
* @since 3.0
@@ -28,8 +28,10 @@ import org.springframework.expression.EvaluationException;
@SuppressWarnings("serial")
public class SpelEvaluationException extends EvaluationException {
- private SpelMessage message;
- private Object[] inserts;
+ private final SpelMessage message;
+
+ private final Object[] inserts;
+
public SpelEvaluationException(SpelMessage message, Object... inserts) {
super(message.formatMessage(0, inserts)); // TODO poor position information, can the callers not really supply something?
@@ -56,15 +58,18 @@ public class SpelEvaluationException extends EvaluationException {
this.inserts = inserts;
}
+
/**
* @return a formatted message with inserts applied
*/
@Override
public String getMessage() {
- if (message != null)
- return message.formatMessage(position, inserts);
- else
+ if (this.message != null) {
+ return this.message.formatMessage(this.position, this.inserts);
+ }
+ else {
return super.getMessage();
+ }
}
/**
@@ -87,7 +92,7 @@ public class SpelEvaluationException extends EvaluationException {
* @return the message inserts
*/
public Object[] getInserts() {
- return inserts;
+ return this.inserts;
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java
index c6d95a9413..b61125af33 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java
@@ -19,98 +19,249 @@ package org.springframework.expression.spel;
import java.text.MessageFormat;
/**
- * Contains all the messages that can be produced by the Spring Expression Language. Each message has a kind (info,
- * warn, error) and a code number. Tests can be written to expect particular code numbers rather than particular text,
- * enabling the message text to more easily be modified and the tests to run successfully in different locales.
- *
- * When a message is formatted, it will have this kind of form
+ * Contains all the messages that can be produced by the Spring Expression Language. Each
+ * message has a kind (info, warn, error) and a code number. Tests can be written to
+ * expect particular code numbers rather than particular text, enabling the message text
+ * to more easily be modified and the tests to run successfully in different locales.
+ *
+ *
When a message is formatted, it will have this kind of form
*
*
* EL1004E: (pos 34): Type cannot be found 'String'
*
*
- * The prefix captures the code and the error kind, whilst the position is included if it is known.
+ * The prefix captures the code and the error kind, whilst the position is
+ * included if it is known.
*
* @author Andy Clement
* @since 3.0
*/
public enum SpelMessage {
- TYPE_CONVERSION_ERROR(Kind.ERROR, 1001, "Type conversion problem, cannot convert from {0} to {1}"), //
- CONSTRUCTOR_NOT_FOUND(Kind.ERROR, 1002, "Constructor call: No suitable constructor found on type {0} for arguments {1}"), //
- CONSTRUCTOR_INVOCATION_PROBLEM(Kind.ERROR, 1003, "A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"), //
- METHOD_NOT_FOUND(Kind.ERROR, 1004, "Method call: Method {0} cannot be found on {1} type"), //
- TYPE_NOT_FOUND(Kind.ERROR, 1005, "Type cannot be found ''{0}''"), //
- FUNCTION_NOT_DEFINED(Kind.ERROR, 1006, "The function ''{0}'' could not be found"), //
- PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL(Kind.ERROR, 1007, "Field or property ''{0}'' cannot be found on null"), //
- PROPERTY_OR_FIELD_NOT_READABLE(Kind.ERROR, 1008, "Field or property ''{0}'' cannot be found on object of type ''{1}''"), //
- PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL(Kind.ERROR, 1009, "Field or property ''{0}'' cannot be set on null"), //
- PROPERTY_OR_FIELD_NOT_WRITABLE(Kind.ERROR, 1010, "Field or property ''{0}'' cannot be set on object of type ''{1}''"), //
- METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1011, "Method call: Attempted to call method {0} on null context object"), //
- CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1012, "Cannot index into a null value"),
- NOT_COMPARABLE(Kind.ERROR, 1013, "Cannot compare instances of {0} and {1}"), //
- INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(Kind.ERROR, 1014, "Incorrect number of arguments for function, {0} supplied but function takes {1}"), //
- INVALID_TYPE_FOR_SELECTION(Kind.ERROR, 1015, "Cannot perform selection on input data of type ''{0}''"), //
- RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(Kind.ERROR, 1016, "Result of selection criteria is not boolean"), //
- BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(Kind.ERROR, 1017, "Right operand for the 'between' operator has to be a two-element list"), //
- INVALID_PATTERN(Kind.ERROR, 1018, "Pattern is not valid ''{0}''"), //
- PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1019, "Projection is not supported on the type ''{0}''"), //
- ARGLIST_SHOULD_NOT_BE_EVALUATED(Kind.ERROR, 1020, "The argument list of a lambda expression should never have getValue() called upon it"), //
- EXCEPTION_DURING_PROPERTY_READ(Kind.ERROR, 1021, "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"), //
- FUNCTION_REFERENCE_CANNOT_BE_INVOKED(Kind.ERROR, 1022, "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"), //
- EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR, 1023, "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"), //
- ARRAY_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1024, "The array has ''{0}'' elements, index ''{1}'' is invalid"), //
- COLLECTION_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1025, "The collection has ''{0}'' elements, index ''{1}'' is invalid"), //
- STRING_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1026, "The string has ''{0}'' characters, index ''{1}'' is invalid"), //
- INDEXING_NOT_SUPPORTED_FOR_TYPE(Kind.ERROR, 1027, "Indexing into type ''{0}'' is not supported"), //
- INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(Kind.ERROR, 1028, "The operator 'instanceof' needs the right operand to be a class, not a ''{0}''"), //
- EXCEPTION_DURING_METHOD_INVOCATION(Kind.ERROR, 1029, "A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"), //
- OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(Kind.ERROR, 1030, "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"), //
- PROBLEM_LOCATING_METHOD(Kind.ERROR, 1031, "Problem locating method {0} cannot on type {1}"),
- SETVALUE_NOT_SUPPORTED( Kind.ERROR, 1032, "setValue(ExpressionState, Object) not supported for ''{0}''"), //
- MULTIPLE_POSSIBLE_METHODS(Kind.ERROR, 1033, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"), //
- EXCEPTION_DURING_PROPERTY_WRITE(Kind.ERROR, 1034, "A problem occurred whilst attempting to set the property ''{0}'': {1}"), //
- NOT_AN_INTEGER(Kind.ERROR, 1035, "The value ''{0}'' cannot be parsed as an int"), //
- NOT_A_LONG(Kind.ERROR, 1036, "The value ''{0}'' cannot be parsed as a long"), //
- INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1037, "First operand to matches operator must be a string. ''{0}'' is not"), //
- INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1038, "Second operand to matches operator must be a string. ''{0}'' is not"), //
- FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1039, "Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static."),//
- NOT_A_REAL(Kind.ERROR, 1040, "The value ''{0}'' cannot be parsed as a double"), //
- MORE_INPUT(Kind.ERROR,1041, "After parsing a valid expression, there is still more data in the expression: ''{0}''"),
- RIGHT_OPERAND_PROBLEM(Kind.ERROR,1042, "Problem parsing right operand"),
- NOT_EXPECTED_TOKEN(Kind.ERROR,1043,"Unexpected token. Expected ''{0}'' but was ''{1}''"),
- OOD(Kind.ERROR,1044,"Unexpectedly ran out of input"), //
- NON_TERMINATING_DOUBLE_QUOTED_STRING(Kind.ERROR,1045,"Cannot find terminating \" for string"),//
- NON_TERMINATING_QUOTED_STRING(Kind.ERROR,1046,"Cannot find terminating ' for string"), //
- MISSING_LEADING_ZERO_FOR_NUMBER(Kind.ERROR,1047,"A real number must be prefixed by zero, it cannot start with just ''.''"), //
- REAL_CANNOT_BE_LONG(Kind.ERROR,1048,"Real number cannot be suffixed with a long (L or l) suffix"),//
- UNEXPECTED_DATA_AFTER_DOT(Kind.ERROR,1049,"Unexpected data after ''.'': ''{0}''"),//
- MISSING_CONSTRUCTOR_ARGS(Kind.ERROR,1050,"The arguments '(...)' for the constructor call are missing"),//
- RUN_OUT_OF_ARGUMENTS(Kind.ERROR,1051,"Unexpected ran out of arguments"),//
- UNABLE_TO_GROW_COLLECTION(Kind.ERROR,1052,"Unable to grow collection"),//
- UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE(Kind.ERROR,1053,"Unable to grow collection: unable to determine list element type"),//
- UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR,1054,"Unable to dynamically create a List to replace a null value"),//
- UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR,1055,"Unable to dynamically create a Map to replace a null value"),//
- UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR,1056,"Unable to dynamically create instance of ''{0}'' to replace a null value"),//
- NO_BEAN_RESOLVER_REGISTERED(Kind.ERROR,1057,"No bean resolver registered in the context to resolve access to bean ''{0}''"),//
- EXCEPTION_DURING_BEAN_RESOLUTION(Kind.ERROR, 1058, "A problem occurred when trying to resolve bean ''{0}'':''{1}''"), //
- INVALID_BEAN_REFERENCE(Kind.ERROR,1059,"@ can only be followed by an identifier or a quoted name"),//
+ TYPE_CONVERSION_ERROR(Kind.ERROR, 1001,
+ "Type conversion problem, cannot convert from {0} to {1}"),
+
+ CONSTRUCTOR_NOT_FOUND(Kind.ERROR, 1002,
+ "Constructor call: No suitable constructor found on type {0} for " +
+ "arguments {1}"),
+
+ CONSTRUCTOR_INVOCATION_PROBLEM(Kind.ERROR, 1003,
+ "A problem occurred whilst attempting to construct an object of type " +
+ "''{0}'' using arguments ''{1}''"),
+
+ METHOD_NOT_FOUND(Kind.ERROR, 1004,
+ "Method call: Method {0} cannot be found on {1} type"),
+
+ TYPE_NOT_FOUND(Kind.ERROR, 1005,
+ "Type cannot be found ''{0}''"),
+
+ FUNCTION_NOT_DEFINED(Kind.ERROR, 1006,
+ "The function ''{0}'' could not be found"),
+
+ PROPERTY_OR_FIELD_NOT_READABLE_ON_NULL(Kind.ERROR, 1007,
+ "Field or property ''{0}'' cannot be found on null"),
+
+ PROPERTY_OR_FIELD_NOT_READABLE(Kind.ERROR, 1008,
+ "Field or property ''{0}'' cannot be found on object of type ''{1}''"),
+
+ PROPERTY_OR_FIELD_NOT_WRITABLE_ON_NULL(Kind.ERROR, 1009,
+ "Field or property ''{0}'' cannot be set on null"),
+
+ PROPERTY_OR_FIELD_NOT_WRITABLE(Kind.ERROR, 1010,
+ "Field or property ''{0}'' cannot be set on object of type ''{1}''"),
+
+ METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED(Kind.ERROR, 1011,
+ "Method call: Attempted to call method {0} on null context object"),
+
+ CANNOT_INDEX_INTO_NULL_VALUE(Kind.ERROR, 1012,
+ "Cannot index into a null value"),
+
+ NOT_COMPARABLE(Kind.ERROR, 1013,
+ "Cannot compare instances of {0} and {1}"),
+
+ INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(Kind.ERROR, 1014,
+ "Incorrect number of arguments for function, {0} supplied but " +
+ "function takes {1}"),
+
+ INVALID_TYPE_FOR_SELECTION(Kind.ERROR, 1015,
+ "Cannot perform selection on input data of type ''{0}''"),
+
+ RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(Kind.ERROR, 1016,
+ "Result of selection criteria is not boolean"),
+
+ BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(Kind.ERROR, 1017,
+ "Right operand for the 'between' operator has to be a two-element list"),
+
+ INVALID_PATTERN(Kind.ERROR, 1018,
+ "Pattern is not valid ''{0}''"),
+
+ PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1019,
+ "Projection is not supported on the type ''{0}''"),
+
+ ARGLIST_SHOULD_NOT_BE_EVALUATED(Kind.ERROR, 1020,
+ "The argument list of a lambda expression should never have getValue() " +
+ "called upon it"),
+
+ EXCEPTION_DURING_PROPERTY_READ(Kind.ERROR, 1021,
+ "A problem occurred whilst attempting to access the property " +
+ "''{0}'': ''{1}''"),
+
+ FUNCTION_REFERENCE_CANNOT_BE_INVOKED(Kind.ERROR, 1022,
+ "The function ''{0}'' mapped to an object of type ''{1}'' which " +
+ "cannot be invoked"),
+
+ EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR, 1023,
+ "A problem occurred whilst attempting to invoke the " +
+ "function ''{0}'': ''{1}''"),
+
+ ARRAY_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1024,
+ "The array has ''{0}'' elements, index ''{1}'' is invalid"),
+
+ COLLECTION_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1025,
+ "The collection has ''{0}'' elements, index ''{1}'' is invalid"),
+
+ STRING_INDEX_OUT_OF_BOUNDS(Kind.ERROR, 1026,
+ "The string has ''{0}'' characters, index ''{1}'' is invalid"),
+
+ INDEXING_NOT_SUPPORTED_FOR_TYPE(Kind.ERROR, 1027,
+ "Indexing into type ''{0}'' is not supported"),
+
+ INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(Kind.ERROR, 1028,
+ "The operator 'instanceof' needs the right operand to be a class, " +
+ "not a ''{0}''"),
+
+ EXCEPTION_DURING_METHOD_INVOCATION(Kind.ERROR, 1029,
+ "A problem occurred when trying to execute method ''{0}'' on object " +
+ "of type ''{1}'': ''{2}''"),
+
+ OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(Kind.ERROR, 1030,
+ "The operator ''{0}'' is not supported between objects of type " +
+ "''{1}'' and ''{2}''"),
+
+ PROBLEM_LOCATING_METHOD(Kind.ERROR, 1031,
+ "Problem locating method {0} cannot on type {1}"),
+
+ SETVALUE_NOT_SUPPORTED( Kind.ERROR, 1032,
+ "setValue(ExpressionState, Object) not supported for ''{0}''"),
+
+ MULTIPLE_POSSIBLE_METHODS(Kind.ERROR, 1033,
+ "Method call of ''{0}'' is ambiguous, supported type conversions " +
+ "allow multiple variants to match"),
+
+ EXCEPTION_DURING_PROPERTY_WRITE(Kind.ERROR, 1034,
+ "A problem occurred whilst attempting to set the property ''{0}'': {1}"),
+
+ NOT_AN_INTEGER(Kind.ERROR, 1035,
+ "The value ''{0}'' cannot be parsed as an int"),
+
+ NOT_A_LONG(Kind.ERROR, 1036,
+ "The value ''{0}'' cannot be parsed as a long"),
+
+ INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1037,
+ "First operand to matches operator must be a string. ''{0}'' is not"),
+
+ INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, 1038,
+ "Second operand to matches operator must be a string. ''{0}'' is not"),
+
+ FUNCTION_MUST_BE_STATIC(Kind.ERROR, 1039,
+ "Only static methods can be called via function references. " +
+ "The method ''{0}'' referred to by name ''{1}'' is not static."),
+
+ NOT_A_REAL(Kind.ERROR, 1040,
+ "The value ''{0}'' cannot be parsed as a double"),
+
+ MORE_INPUT(Kind.ERROR,1041,
+ "After parsing a valid expression, there is still more data in " +
+ "the expression: ''{0}''"),
+
+ RIGHT_OPERAND_PROBLEM(Kind.ERROR, 1042,
+ "Problem parsing right operand"),
+
+ NOT_EXPECTED_TOKEN(Kind.ERROR, 1043,
+ "Unexpected token. Expected ''{0}'' but was ''{1}''"),
+
+ OOD(Kind.ERROR, 1044,
+ "Unexpectedly ran out of input"),
+
+ NON_TERMINATING_DOUBLE_QUOTED_STRING(Kind.ERROR, 1045,
+ "Cannot find terminating \" for string"),
+
+ NON_TERMINATING_QUOTED_STRING(Kind.ERROR, 1046,
+ "Cannot find terminating ' for string"),
+
+ MISSING_LEADING_ZERO_FOR_NUMBER(Kind.ERROR, 1047,
+ "A real number must be prefixed by zero, it cannot start with just ''.''"),
+
+ REAL_CANNOT_BE_LONG(Kind.ERROR, 1048,
+ "Real number cannot be suffixed with a long (L or l) suffix"),
+
+ UNEXPECTED_DATA_AFTER_DOT(Kind.ERROR, 1049,
+ "Unexpected data after ''.'': ''{0}''"),
+
+ MISSING_CONSTRUCTOR_ARGS(Kind.ERROR, 1050,
+ "The arguments '(...)' for the constructor call are missing"),
+
+ RUN_OUT_OF_ARGUMENTS(Kind.ERROR, 1051,
+ "Unexpected ran out of arguments"),
+
+ UNABLE_TO_GROW_COLLECTION(Kind.ERROR, 1052,
+ "Unable to grow collection"),
+
+ UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE(Kind.ERROR, 1053,
+ "Unable to grow collection: unable to determine list element type"),
+
+ UNABLE_TO_CREATE_LIST_FOR_INDEXING(Kind.ERROR, 1054,
+ "Unable to dynamically create a List to replace a null value"),
+
+ UNABLE_TO_CREATE_MAP_FOR_INDEXING(Kind.ERROR, 1055,
+ "Unable to dynamically create a Map to replace a null value"),
+
+ UNABLE_TO_DYNAMICALLY_CREATE_OBJECT(Kind.ERROR, 1056,
+ "Unable to dynamically create instance of ''{0}'' to replace a null value"),
+
+ NO_BEAN_RESOLVER_REGISTERED(Kind.ERROR, 1057,
+ "No bean resolver registered in the context to resolve access to bean ''{0}''"),
+
+ EXCEPTION_DURING_BEAN_RESOLUTION(Kind.ERROR, 1058,
+ "A problem occurred when trying to resolve bean ''{0}'':''{1}''"),
+
+ INVALID_BEAN_REFERENCE(Kind.ERROR, 1059,
+ "@ can only be followed by an identifier or a quoted name"),
+
TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(Kind.ERROR, 1060,
- "Expected the type of the new array to be specified as a String but found ''{0}''"), //
+ "Expected the type of the new array to be specified as a String but found ''{0}''"),
+
INCORRECT_ELEMENT_TYPE_FOR_ARRAY(Kind.ERROR, 1061,
- "The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"), //
+ "The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"),
+
MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED(Kind.ERROR, 1062,
- "Using an initializer to build a multi-dimensional array is not currently supported"), //
- MISSING_ARRAY_DIMENSION(Kind.ERROR, 1063, "A required array dimension has not been specified"), //
- INITIALIZER_LENGTH_INCORRECT(
- Kind.ERROR, 1064, "array initializer size does not match array dimensions"), //
- UNEXPECTED_ESCAPE_CHAR(Kind.ERROR,1065,"unexpected escape character."), //
- OPERAND_NOT_INCREMENTABLE(Kind.ERROR,1066,"the expression component ''{0}'' does not support increment"), //
- OPERAND_NOT_DECREMENTABLE(Kind.ERROR,1067,"the expression component ''{0}'' does not support decrement"), //
- NOT_ASSIGNABLE(Kind.ERROR,1068,"the expression component ''{0}'' is not assignable"), //
- MISSING_CHARACTER(Kind.ERROR,1069,"missing expected character ''{0}''"),
- LEFT_OPERAND_PROBLEM(Kind.ERROR,1070, "Problem parsing left operand"),
- MISSING_SELECTION_EXPRESSION(Kind.ERROR, 1071, "A required selection expression has not been specified");
+ "Using an initializer to build a multi-dimensional array is not currently supported"),
+
+ MISSING_ARRAY_DIMENSION(Kind.ERROR, 1063,
+ "A required array dimension has not been specified"),
+
+ INITIALIZER_LENGTH_INCORRECT(Kind.ERROR, 1064,
+ "array initializer size does not match array dimensions"),
+
+ UNEXPECTED_ESCAPE_CHAR(Kind.ERROR, 1065, "unexpected escape character."),
+
+ OPERAND_NOT_INCREMENTABLE(Kind.ERROR, 1066,
+ "the expression component ''{0}'' does not support increment"),
+
+ OPERAND_NOT_DECREMENTABLE(Kind.ERROR, 1067,
+ "the expression component ''{0}'' does not support decrement"),
+
+ NOT_ASSIGNABLE(Kind.ERROR, 1068,
+ "the expression component ''{0}'' is not assignable"),
+
+ MISSING_CHARACTER(Kind.ERROR, 1069,
+ "missing expected character ''{0}''"),
+
+ LEFT_OPERAND_PROBLEM(Kind.ERROR, 1070,
+ "Problem parsing left operand"),
+
+ MISSING_SELECTION_EXPRESSION(Kind.ERROR, 1071,
+ "A required selection expression has not been specified");
private Kind kind;
private int code;
@@ -134,23 +285,17 @@ public enum SpelMessage {
*/
public String formatMessage(int pos, Object... inserts) {
StringBuilder formattedMessage = new StringBuilder();
- formattedMessage.append("EL").append(code);
- switch (kind) {
-// case WARNING:
-// formattedMessage.append("W");
-// break;
-// case INFO:
-// formattedMessage.append("I");
-// break;
- case ERROR:
- formattedMessage.append("E");
- break;
+ formattedMessage.append("EL").append(this.code);
+ switch (this.kind) {
+ case ERROR:
+ formattedMessage.append("E");
+ break;
}
formattedMessage.append(":");
if (pos != -1) {
formattedMessage.append("(pos ").append(pos).append("): ");
}
- formattedMessage.append(MessageFormat.format(message, inserts));
+ formattedMessage.append(MessageFormat.format(this.message, inserts));
return formattedMessage.toString();
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java
index 6e2ba93f3f..0699445702 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelNode.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,14 +28,16 @@ import org.springframework.expression.TypedValue;
public interface SpelNode {
/**
- * Evaluate the expression node in the context of the supplied expression state and return the value.
+ * Evaluate the expression node in the context of the supplied expression state and
+ * return the value.
* @param expressionState the current expression state (includes the context)
* @return the value of this node evaluated against the specified state
*/
Object getValue(ExpressionState expressionState) throws EvaluationException;
/**
- * Evaluate the expression node in the context of the supplied expression state and return the typed value.
+ * Evaluate the expression node in the context of the supplied expression state and
+ * return the typed value.
* @param expressionState the current expression state (includes the context)
* @return the type value of this node evaluated against the specified state
*/
@@ -51,11 +53,13 @@ public interface SpelNode {
boolean isWritable(ExpressionState expressionState) throws EvaluationException;
/**
- * Evaluate the expression to a node and then set the new value on that node. For example, if the expression
- * evaluates to a property reference then the property will be set to the new value.
+ * Evaluate the expression to a node and then set the new value on that node. For
+ * example, if the expression evaluates to a property reference then the property will
+ * be set to the new value.
* @param expressionState the current expression state (includes the context)
* @param newValue the new value
- * @throws EvaluationException if any problem occurs evaluating the expression or setting the new value
+ * @throws EvaluationException if any problem occurs evaluating the expression or
+ * setting the new value
*/
void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException;
@@ -78,7 +82,8 @@ public interface SpelNode {
/**
* Determine the class of the object passed in, unless it is already a class object.
* @param obj the object that the caller wants the class of
- * @return the class of the object if it is not already a class object, or null if the object is null
+ * @return the class of the object if it is not already a class object, or null if the
+ * object is null
*/
Class> getObjectClass(Object obj);
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java
index 1935b3ba61..2a623ddccc 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,24 +19,20 @@ import org.springframework.expression.ParseException;
/**
- * Root exception for Spring EL related exceptions. Rather than holding a hard coded string indicating the problem, it
- * records a message key and the inserts for the message. See {@link SpelMessage} for the list of all possible messages
- * that can occur.
- *
+ * Root exception for Spring EL related exceptions. Rather than holding a hard coded
+ * string indicating the problem, it records a message key and the inserts for the
+ * message. See {@link SpelMessage} for the list of all possible messages that can occur.
+ *
* @author Andy Clement
* @since 3.0
*/
@SuppressWarnings("serial")
public class SpelParseException extends ParseException {
- private SpelMessage message;
- private Object[] inserts;
+ private final SpelMessage message;
+
+ private final Object[] inserts;
-// public SpelParseException(String expressionString, int position, Throwable cause, SpelMessages message, Object... inserts) {
-// super(expressionString, position, message.formatMessage(position,inserts), cause);
-// this.message = message;
-// this.inserts = inserts;
-// }
public SpelParseException(String expressionString, int position, SpelMessage message, Object... inserts) {
super(expressionString, position, message.formatMessage(position,inserts));
@@ -59,36 +55,14 @@ public class SpelParseException extends ParseException {
this.inserts = inserts;
}
-//
-// public SpelException(Throwable cause, SpelMessages message, Object... inserts) {
-// super(cause);
-// this.message = message;
-// this.inserts = inserts;
-// }
-//
-// public SpelException(int position, SpelMessages message, Object... inserts) {
-// super((Throwable)null);
-// this.position = position;
-// this.message = message;
-// this.inserts = inserts;
-// }
-//
-// public SpelException(SpelMessages message, Object... inserts) {
-// super((Throwable)null);
-// this.message = message;
-// this.inserts = inserts;
-// }
-
/**
* @return a formatted message with inserts applied
*/
@Override
public String getMessage() {
- if (message != null)
- return message.formatMessage(position, inserts);
- else
- return super.getMessage();
+ return (this.message != null ? this.message.formatMessage(this.position, this.inserts)
+ : super.getMessage());
}
/**
@@ -102,7 +76,7 @@ public class SpelParseException extends ParseException {
* @return the message inserts
*/
public Object[] getInserts() {
- return inserts;
+ return this.inserts;
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java
index ccadb93061..20932fa9b2 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParserConfiguration.java
@@ -30,7 +30,7 @@ public class SpelParserConfiguration {
private final boolean autoGrowCollections;
- private int maximumAutoGrowSize;
+ private final int maximumAutoGrowSize;
/**
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java
index c472ed6393..7d2c045121 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Assign.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
- * Represents assignment. An alternative to calling setValue() for an expression is to use an assign.
+ * Represents assignment. An alternative to calling setValue() for an expression is to use
+ * an assign.
*
* Example: 'someNumberProperty=42'
*
@@ -30,21 +31,23 @@ import org.springframework.expression.spel.ExpressionState;
*/
public class Assign extends SpelNodeImpl {
+
public Assign(int pos,SpelNodeImpl... operands) {
super(pos,operands);
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
- TypedValue newValue = children[1].getValueInternal(state);
+ TypedValue newValue = this.children[1].getValueInternal(state);
getChild(0).setValue(state, newValue.getValue());
return newValue;
}
@Override
public String toStringAST() {
- return new StringBuilder().append(getChild(0).toStringAST()).append("=").append(getChild(1).toStringAST())
- .toString();
+ return new StringBuilder().append(getChild(0).toStringAST()).append("=").append(
+ getChild(1).toStringAST()).toString();
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java
index 92eb3036fa..98cccc4985 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/AstUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,13 +30,15 @@ import org.springframework.expression.PropertyAccessor;
public class AstUtils {
/**
- * Determines the set of property resolvers that should be used to try and access a property on the specified target
- * type. The resolvers are considered to be in an ordered list, however in the returned list any that are exact
- * matches for the input target type (as opposed to 'general' resolvers that could work for any type) are placed at
- * the start of the list. In addition, there are specific resolvers that exactly name the class in question and
- * resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the
- * specific resolvers set and will be tried after exactly matching accessors but before generic accessors.
- *
+ * Determines the set of property resolvers that should be used to try and access a
+ * property on the specified target type. The resolvers are considered to be in an
+ * ordered list, however in the returned list any that are exact matches for the input
+ * target type (as opposed to 'general' resolvers that could work for any type) are
+ * placed at the start of the list. In addition, there are specific resolvers that
+ * exactly name the class in question and resolvers that name a specific class but it
+ * is a supertype of the class we have. These are put at the end of the specific
+ * resolvers set and will be tried after exactly matching accessors but before generic
+ * accessors.
* @param targetType the type upon which property access is being attempted
* @return a list of resolvers that should be tried in order to access the property
*/
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java
index 35636fe806..b0be644e1f 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,25 +31,31 @@ import org.springframework.expression.spel.SpelMessage;
*/
public class BeanReference extends SpelNodeImpl {
- private String beanname;
+ private final String beanname;
+
public BeanReference(int pos,String beanname) {
super(pos);
this.beanname = beanname;
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
BeanResolver beanResolver = state.getEvaluationContext().getBeanResolver();
if (beanResolver==null) {
- throw new SpelEvaluationException(getStartPosition(),SpelMessage.NO_BEAN_RESOLVER_REGISTERED, beanname);
+ throw new SpelEvaluationException(getStartPosition(),
+ SpelMessage.NO_BEAN_RESOLVER_REGISTERED, this.beanname);
}
+
try {
- TypedValue bean = new TypedValue(beanResolver.resolve(state.getEvaluationContext(),beanname));
+ TypedValue bean = new TypedValue(beanResolver.resolve(
+ state.getEvaluationContext(), this.beanname));
return bean;
- } catch (AccessException ae) {
+ }
+ catch (AccessException ae) {
throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_BEAN_RESOLUTION,
- beanname, ae.getMessage());
+ this.beanname, ae.getMessage());
}
}
@@ -57,10 +63,11 @@ public class BeanReference extends SpelNodeImpl {
public String toStringAST() {
StringBuilder sb = new StringBuilder();
sb.append("@");
- if (beanname.indexOf('.')==-1) {
- sb.append(beanname);
- } else {
- sb.append("'").append(beanname).append("'");
+ if (this.beanname.indexOf('.') == -1) {
+ sb.append(this.beanname);
+ }
+ else {
+ sb.append("'").append(this.beanname).append("'");
}
return sb.toString();
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java
index 84878ee131..5aa748c8c0 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,11 +27,13 @@ public class BooleanLiteral extends Literal {
private final BooleanTypedValue value;
+
public BooleanLiteral(String payload, int pos, boolean value) {
super(payload, pos);
this.value = BooleanTypedValue.forValue(value);
}
+
@Override
public BooleanTypedValue getLiteralValue() {
return this.value;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java
index b632adec5e..688670c142 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,32 +39,35 @@ public class CompoundExpression extends SpelNodeImpl {
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
- if (getChildCount()==1) {
- return children[0].getValueRef(state);
+ if (getChildCount() == 1) {
+ return this.children[0].getValueRef(state);
}
TypedValue result = null;
SpelNodeImpl nextNode = null;
try {
- nextNode = children[0];
+ nextNode = this.children[0];
result = nextNode.getValueInternal(state);
int cc = getChildCount();
- for (int i = 1; i < cc-1; i++) {
+ for (int i = 1; i < cc - 1; i++) {
try {
state.pushActiveContextObject(result);
- nextNode = children[i];
+ nextNode = this.children[i];
result = nextNode.getValueInternal(state);
- } finally {
+ }
+ finally {
state.popActiveContextObject();
}
}
try {
state.pushActiveContextObject(result);
- nextNode = children[cc-1];
+ nextNode = this.children[cc-1];
return nextNode.getValueRef(state);
- } finally {
+ }
+ finally {
state.popActiveContextObject();
}
- } catch (SpelEvaluationException ee) {
+ }
+ catch (SpelEvaluationException ee) {
// Correct the position for the error before re-throwing
ee.setPosition(nextNode.getStartPosition());
throw ee;
@@ -96,7 +99,9 @@ public class CompoundExpression extends SpelNodeImpl {
public String toStringAST() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
- if (i>0) { sb.append("."); }
+ if (i > 0) {
+ sb.append(".");
+ }
sb.append(getChild(i).toStringAST());
}
return sb.toString();
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java
index c6a25931fb..a16cc55788 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -132,7 +132,8 @@ public class ConstructorReference extends SpelNodeImpl {
Throwable rootCause = ae.getCause().getCause();
if (rootCause instanceof RuntimeException) {
throw (RuntimeException) rootCause;
- } else {
+ }
+ else {
String typename = (String) this.children[0].getValueInternal(state).getValue();
throw new SpelEvaluationException(getStartPosition(), rootCause,
SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename, FormatHelper
@@ -153,9 +154,9 @@ public class ConstructorReference extends SpelNodeImpl {
return executorToUse.execute(state.getEvaluationContext(), arguments);
}
catch (AccessException ae) {
- throw new SpelEvaluationException(getStartPosition(), ae, SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM,
- typename, FormatHelper.formatMethodForMessage("", argumentTypes));
-
+ throw new SpelEvaluationException(getStartPosition(), ae,
+ SpelMessage.CONSTRUCTOR_INVOCATION_PROBLEM, typename,
+ FormatHelper.formatMethodForMessage("", argumentTypes));
}
}
@@ -168,8 +169,9 @@ public class ConstructorReference extends SpelNodeImpl {
* @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
* @throws SpelEvaluationException if there is a problem locating the constructor
*/
- private ConstructorExecutor findExecutorForConstructor(String typename, List argumentTypes,
- ExpressionState state) throws SpelEvaluationException {
+ private ConstructorExecutor findExecutorForConstructor(String typename,
+ List argumentTypes, ExpressionState state)
+ throws SpelEvaluationException {
EvaluationContext eContext = state.getEvaluationContext();
List cResolvers = eContext.getConstructorResolvers();
@@ -202,8 +204,9 @@ public class ConstructorReference extends SpelNodeImpl {
sb.append(getChild(index++).toStringAST());
sb.append("(");
for (int i = index; i < getChildCount(); i++) {
- if (i > index)
+ if (i > index) {
sb.append(",");
+ }
sb.append(getChild(i).toStringAST());
}
sb.append(")");
@@ -221,8 +224,8 @@ public class ConstructorReference extends SpelNodeImpl {
Object intendedArrayType = getChild(0).getValue(state);
if (!(intendedArrayType instanceof String)) {
throw new SpelEvaluationException(getChild(0).getStartPosition(),
- SpelMessage.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION, FormatHelper
- .formatClassNameForMessage(intendedArrayType.getClass()));
+ SpelMessage.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION,
+ FormatHelper.formatClassNameForMessage(intendedArrayType.getClass()));
}
String type = (String) intendedArrayType;
Class> componentType;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java
index b37548a5a2..7c44d0635c 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Elvis.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
- * Represents the elvis operator ?:. For an expression "a?:b" if a is not null, the value of the expression
- * is "a", if a is null then the value of the expression is "b".
+ * Represents the elvis operator ?:. For an expression "a?:b" if a is not null, the value
+ * of the expression is "a", if a is null then the value of the expression is "b".
*
* @author Andy Clement
* @since 3.0
@@ -33,25 +33,30 @@ public class Elvis extends SpelNodeImpl {
super(pos,args);
}
+
/**
- * Evaluate the condition and if not null, return it. If it is null return the other value.
+ * Evaluate the condition and if not null, return it. If it is null return the other
+ * value.
* @param state the expression state
- * @throws EvaluationException if the condition does not evaluate correctly to a boolean or there is a problem
- * executing the chosen alternative
+ * @throws EvaluationException if the condition does not evaluate correctly to a
+ * boolean or there is a problem executing the chosen alternative
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
- TypedValue value = children[0].getValueInternal(state);
- if (value.getValue()!=null && !((value.getValue() instanceof String) && ((String)value.getValue()).length()==0)) {
+ TypedValue value = this.children[0].getValueInternal(state);
+ if ((value.getValue() != null) && !((value.getValue() instanceof String) &&
+ ((String) value.getValue()).length() == 0)) {
return value;
- } else {
- return children[1].getValueInternal(state);
+ }
+ else {
+ return this.children[1].getValueInternal(state);
}
}
@Override
public String toStringAST() {
- return new StringBuilder().append(getChild(0).toStringAST()).append(" ?: ").append(getChild(1).toStringAST()).toString();
+ return new StringBuilder().append(getChild(0).toStringAST()).append(" ?: ").append(
+ getChild(1).toStringAST()).toString();
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java
index c63ee9a8ce..6d7be2b481 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
@@ -24,6 +25,7 @@ import org.springframework.expression.TypedValue;
* @since 3.2
*/
public class FloatLiteral extends Literal {
+
private final TypedValue value;
FloatLiteral(String payload, int pos, float value) {
@@ -31,6 +33,7 @@ public class FloatLiteral extends Literal {
this.value = new TypedValue(value);
}
+
@Override
public TypedValue getLiteralValue() {
return this.value;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java
index 1d3b853e29..8719f49526 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FormatHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -75,7 +75,8 @@ public class FormatHelper {
for (int i = 0; i < dims; i++) {
fmtd.append("[]");
}
- } else {
+ }
+ else {
fmtd.append(clazz.getName());
}
return fmtd.toString();
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java
index db69a05945..642c4497ee 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,14 +31,15 @@ import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.util.ReflectionUtils;
/**
- * A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in the context prior to the
- * expression being evaluated or within the expression itself using a lambda function definition. For example: Lambda
- * function definition in an expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function:
- * "#isEven(37)". Functions may also be static java methods, registered in the context prior to invocation of the
- * expression.
+ * A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in
+ * the context prior to the expression being evaluated or within the expression itself
+ * using a lambda function definition. For example: Lambda function definition in an
+ * expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function:
+ * "#isEven(37)". Functions may also be static java methods, registered in the context
+ * prior to invocation of the expression.
*
- * Functions are very simplistic, the arguments are not part of the definition (right now),
- * so the names must be unique.
+ *
Functions are very simplistic, the arguments are not part of the definition (right
+ * now), so the names must be unique.
*
* @author Andy Clement
* @since 3.0
@@ -47,21 +48,23 @@ public class FunctionReference extends SpelNodeImpl {
private final String name;
+
public FunctionReference(String functionName, int pos, SpelNodeImpl... arguments) {
super(pos,arguments);
- name = functionName;
+ this.name = functionName;
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
- TypedValue o = state.lookupVariable(name);
+ TypedValue o = state.lookupVariable(this.name);
if (o == null) {
- throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, name);
+ throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_NOT_DEFINED, this.name);
}
// Two possibilities: a lambda function or a Java static method registered as a function
if (!(o.getValue() instanceof Method)) {
- throw new SpelEvaluationException(SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, name, o.getClass());
+ throw new SpelEvaluationException(SpelMessage.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, this.name, o.getClass());
}
try {
return executeFunctionJLRMethod(state, (Method) o.getValue());
@@ -89,9 +92,9 @@ public class FunctionReference extends SpelNodeImpl {
}
// Only static methods can be called in this way
if (!Modifier.isStatic(method.getModifiers())) {
- throw new SpelEvaluationException(getStartPosition(), SpelMessage.FUNCTION_MUST_BE_STATIC, method
- .getDeclaringClass().getName()
- + "." + method.getName(), name);
+ throw new SpelEvaluationException(getStartPosition(),
+ SpelMessage.FUNCTION_MUST_BE_STATIC,
+ method.getDeclaringClass().getName() + "." + method.getName(), this.name);
}
// Convert arguments if necessary and remap them for varargs if required
@@ -100,7 +103,8 @@ public class FunctionReference extends SpelNodeImpl {
ReflectionHelper.convertAllArguments(converter, functionArgs, method);
}
if (method.isVarArgs()) {
- functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(method.getParameterTypes(), functionArgs);
+ functionArgs = ReflectionHelper.setupArgumentsForVarargsInvocation(
+ method.getParameterTypes(), functionArgs);
}
try {
@@ -116,11 +120,12 @@ public class FunctionReference extends SpelNodeImpl {
@Override
public String toStringAST() {
- StringBuilder sb = new StringBuilder("#").append(name);
+ StringBuilder sb = new StringBuilder("#").append(this.name);
sb.append("(");
for (int i = 0; i < getChildCount(); i++) {
- if (i > 0)
+ if (i > 0) {
sb.append(",");
+ }
sb.append(getChild(i).toStringAST());
}
sb.append(")");
@@ -137,7 +142,7 @@ public class FunctionReference extends SpelNodeImpl {
// Compute arguments to the function
Object[] arguments = new Object[getChildCount()];
for (int i = 0; i < arguments.length; i++) {
- arguments[i] = children[i].getValueInternal(state).getValue();
+ arguments[i] = this.children[i].getValueInternal(state).getValue();
}
return arguments;
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java
index e174535301..d258fbb993 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Identifier.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,14 +27,16 @@ public class Identifier extends SpelNodeImpl {
private final TypedValue id;
+
public Identifier(String payload,int pos) {
super(pos);
this.id = new TypedValue(payload);
}
+
@Override
public String toStringAST() {
- return (String)this.id.getValue();
+ return (String) this.id.getValue();
}
@Override
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
index 4f919beec2..82e7db4259 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java
@@ -33,9 +33,8 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
/**
- * An Indexer can index into some proceeding structure to access a particular
- * piece of it. Supported structures are: strings/collections
- * (lists/sets)/arrays
+ * An Indexer can index into some proceeding structure to access a particular piece of it.
+ * Supported structures are: strings/collections (lists/sets)/arrays
*
* @author Andy Clement
* @author Phillip Webb
@@ -95,26 +94,28 @@ public class Indexer extends SpelNodeImpl {
private final Object array;
- private final int idx;
+ private final int index;
private final TypeDescriptor typeDescriptor;
- ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int idx, TypeDescriptor typeDescriptor) {
+
+ ArrayIndexingValueRef(TypeConverter typeConverter, Object array, int index, TypeDescriptor typeDescriptor) {
this.typeConverter = typeConverter;
this.array = array;
- this.idx = idx;
+ this.index = index;
this.typeDescriptor = typeDescriptor;
}
+
@Override
public TypedValue getValue() {
- Object arrayElement = accessArrayElement(this.array, this.idx);
+ Object arrayElement = accessArrayElement(this.array, this.index);
return new TypedValue(arrayElement, this.typeDescriptor.elementTypeDescriptor(arrayElement));
}
@Override
public void setValue(Object newValue) {
- setArrayElement(this.typeConverter, this.array, this.idx, newValue,
+ setArrayElement(this.typeConverter, this.array, this.index, newValue,
this.typeDescriptor.getElementTypeDescriptor().getType());
}
@@ -136,17 +137,21 @@ public class Indexer extends SpelNodeImpl {
private final TypeDescriptor mapEntryTypeDescriptor;
- MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key, TypeDescriptor mapEntryTypeDescriptor) {
+
+ MapIndexingValueRef(TypeConverter typeConverter, Map map, Object key,
+ TypeDescriptor mapEntryTypeDescriptor) {
this.typeConverter = typeConverter;
this.map = map;
this.key = key;
this.mapEntryTypeDescriptor = mapEntryTypeDescriptor;
}
+
@Override
public TypedValue getValue() {
Object value = this.map.get(this.key);
- return new TypedValue(value, this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value));
+ return new TypedValue(value,
+ this.mapEntryTypeDescriptor.getMapValueTypeDescriptor(value));
}
@Override
@@ -171,71 +176,75 @@ public class Indexer extends SpelNodeImpl {
private final String name;
- private final EvaluationContext eContext;
+ private final EvaluationContext evaluationContext;
+
+ private final TypeDescriptor targetObjectTypeDescriptor;
- private final TypeDescriptor td;
public PropertyIndexingValueRef(Object targetObject, String value, EvaluationContext evaluationContext,
TypeDescriptor targetObjectTypeDescriptor) {
this.targetObject = targetObject;
this.name = value;
- this.eContext = evaluationContext;
- this.td = targetObjectTypeDescriptor;
+ this.evaluationContext = evaluationContext;
+ this.targetObjectTypeDescriptor = targetObjectTypeDescriptor;
}
+
@Override
public TypedValue getValue() {
- Class> targetObjectRuntimeClass = getObjectClass(targetObject);
+ Class> targetObjectRuntimeClass = getObjectClass(this.targetObject);
try {
- if (cachedReadName != null && cachedReadName.equals(name) && cachedReadTargetType != null &&
- cachedReadTargetType.equals(targetObjectRuntimeClass)) {
+ if (Indexer.this.cachedReadName != null && Indexer.this.cachedReadName.equals(this.name) && Indexer.this.cachedReadTargetType != null &&
+ Indexer.this.cachedReadTargetType.equals(targetObjectRuntimeClass)) {
// it is OK to use the cached accessor
- return cachedReadAccessor.read(this.eContext, this.targetObject, this.name);
+ return Indexer.this.cachedReadAccessor.read(this.evaluationContext, this.targetObject, this.name);
}
- List accessorsToTry =
- AstUtils.getPropertyAccessorsToTry(targetObjectRuntimeClass, eContext.getPropertyAccessors());
+
+ List accessorsToTry = AstUtils.getPropertyAccessorsToTry(
+ targetObjectRuntimeClass, this.evaluationContext.getPropertyAccessors());
+
if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) {
- if (accessor.canRead(this.eContext, this.targetObject, this.name)) {
+ if (accessor.canRead(this.evaluationContext, this.targetObject, this.name)) {
if (accessor instanceof ReflectivePropertyAccessor) {
accessor = ((ReflectivePropertyAccessor) accessor).createOptimalAccessor(
- this.eContext, this.targetObject, this.name);
+ this.evaluationContext, this.targetObject, this.name);
}
- cachedReadAccessor = accessor;
- cachedReadName = this.name;
- cachedReadTargetType = targetObjectRuntimeClass;
- return accessor.read(this.eContext, this.targetObject, this.name);
+ Indexer.this.cachedReadAccessor = accessor;
+ Indexer.this.cachedReadName = this.name;
+ Indexer.this.cachedReadTargetType = targetObjectRuntimeClass;
+ return accessor.read(this.evaluationContext, this.targetObject, this.name);
}
}
}
}
catch (AccessException ex) {
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
- this.td.toString());
+ this.targetObjectTypeDescriptor.toString());
}
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
- this.td.toString());
+ this.targetObjectTypeDescriptor.toString());
}
@Override
public void setValue(Object newValue) {
- Class> contextObjectClass = getObjectClass(targetObject);
+ Class> contextObjectClass = getObjectClass(this.targetObject);
try {
- if (cachedWriteName != null && cachedWriteName.equals(name) && cachedWriteTargetType != null &&
- cachedWriteTargetType.equals(contextObjectClass)) {
+ if (Indexer.this.cachedWriteName != null && Indexer.this.cachedWriteName.equals(this.name) && Indexer.this.cachedWriteTargetType != null &&
+ Indexer.this.cachedWriteTargetType.equals(contextObjectClass)) {
// it is OK to use the cached accessor
- cachedWriteAccessor.write(this.eContext, this.targetObject, this.name, newValue);
+ Indexer.this.cachedWriteAccessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
return;
}
List accessorsToTry =
- AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.eContext.getPropertyAccessors());
+ AstUtils.getPropertyAccessorsToTry(contextObjectClass, this.evaluationContext.getPropertyAccessors());
if (accessorsToTry != null) {
for (PropertyAccessor accessor : accessorsToTry) {
- if (accessor.canWrite(this.eContext, this.targetObject, this.name)) {
- cachedWriteName = this.name;
- cachedWriteTargetType = contextObjectClass;
- cachedWriteAccessor = accessor;
- accessor.write(this.eContext, this.targetObject, this.name, newValue);
+ if (accessor.canWrite(this.evaluationContext, this.targetObject, this.name)) {
+ Indexer.this.cachedWriteName = this.name;
+ Indexer.this.cachedWriteTargetType = contextObjectClass;
+ Indexer.this.cachedWriteAccessor = accessor;
+ accessor.write(this.evaluationContext, this.targetObject, this.name, newValue);
return;
}
}
@@ -267,7 +276,8 @@ public class Indexer extends SpelNodeImpl {
private final boolean growCollection;
- private int maximumSize;
+ private final int maximumSize;
+
CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor,
TypeConverter typeConverter, boolean growCollection, int maximumSize) {
@@ -279,6 +289,7 @@ public class Indexer extends SpelNodeImpl {
this.maximumSize = maximumSize;
}
+
@Override
public TypedValue getValue() {
growCollectionIfNecessary();
@@ -356,19 +367,21 @@ public class Indexer extends SpelNodeImpl {
private final int index;
- private final TypeDescriptor td;
+ private final TypeDescriptor typeDescriptor;
- public StringIndexingLValue(String target, int index, TypeDescriptor td) {
+
+ public StringIndexingLValue(String target, int index, TypeDescriptor typeDescriptor) {
this.target = target;
this.index = index;
- this.td = td;
+ this.typeDescriptor = typeDescriptor;
}
+
@Override
public TypedValue getValue() {
if (this.index >= this.target.length()) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.STRING_INDEX_OUT_OF_BOUNDS,
- this.target.length(), index);
+ this.target.length(), this.index);
}
return new TypedValue(String.valueOf(this.target.charAt(this.index)));
}
@@ -376,7 +389,7 @@ public class Indexer extends SpelNodeImpl {
@Override
public void setValue(Object newValue) {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.INDEXING_NOT_SUPPORTED_FOR_TYPE,
- this.td.toString());
+ this.typeDescriptor.toString());
}
@Override
@@ -387,6 +400,7 @@ public class Indexer extends SpelNodeImpl {
@Override
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
+
TypedValue context = state.getActiveContextObject();
Object targetObject = context.getValue();
TypeDescriptor targetObjectTypeDescriptor = context.getTypeDescriptor();
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java
index 1839d203c4..cae9002f40 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.springframework.expression.spel.ast;
import java.util.ArrayList;
@@ -35,11 +36,13 @@ public class InlineList extends SpelNodeImpl {
// if the list is purely literals, it is a constant value and can be computed and cached
TypedValue constant = null; // TODO must be immutable list
+
public InlineList(int pos, SpelNodeImpl... args) {
super(pos, args);
checkIfConstant();
}
+
/**
* If all the components of the list are constants, or lists that themselves contain constants, then a constant list
* can be built to represent this node. This will speed up later getValue calls and reduce the amount of garbage
@@ -55,7 +58,8 @@ public class InlineList extends SpelNodeImpl {
if (!inlineList.isConstant()) {
isConstant = false;
}
- } else {
+ }
+ else {
isConstant = false;
}
}
@@ -67,7 +71,8 @@ public class InlineList extends SpelNodeImpl {
SpelNode child = getChild(c);
if ((child instanceof Literal)) {
constantList.add(((Literal) child).getLiteralValue().getValue());
- } else if (child instanceof InlineList) {
+ }
+ else if (child instanceof InlineList) {
constantList.add(((InlineList) child).getConstantValue());
}
}
@@ -77,9 +82,10 @@ public class InlineList extends SpelNodeImpl {
@Override
public TypedValue getValueInternal(ExpressionState expressionState) throws EvaluationException {
- if (constant != null) {
- return constant;
- } else {
+ if (this.constant != null) {
+ return this.constant;
+ }
+ else {
List returnValue = new ArrayList();
int childcount = getChildCount();
for (int c = 0; c < childcount; c++) {
@@ -109,12 +115,12 @@ public class InlineList extends SpelNodeImpl {
* @return whether this list is a constant value
*/
public boolean isConstant() {
- return constant != null;
+ return this.constant != null;
}
@SuppressWarnings("unchecked")
private List getConstantValue() {
- return (List) constant.getValue();
+ return (List) this.constant.getValue();
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java
index cafbd17d28..a648144901 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,11 +27,13 @@ public class IntLiteral extends Literal {
private final TypedValue value;
+
IntLiteral(String payload, int pos, int value) {
super(payload, pos);
this.value = new TypedValue(value);
}
+
@Override
public TypedValue getLiteralValue() {
return this.value;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java
index 3d69e3fa10..3404411dbb 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,11 +28,13 @@ public class LongLiteral extends Literal {
private final TypedValue value;
+
LongLiteral(String payload, int pos, long value) {
super(payload, pos);
this.value = new TypedValue(value);
}
+
@Override
public TypedValue getLiteralValue() {
return this.value;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java
index 26c5179005..1e88cebc96 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java
@@ -18,6 +18,7 @@ package org.springframework.expression.spel.ast;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import org.springframework.core.convert.TypeDescriptor;
@@ -33,6 +34,8 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
/**
+ * Expression language AST node that represents a method reference.
+ *
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
@@ -43,7 +46,7 @@ public class MethodReference extends SpelNodeImpl {
private final boolean nullSafe;
- private volatile MethodExecutor cachedExecutor;
+ private volatile CachedMethodExecutor cachedExecutor;
public MethodReference(boolean nullSafe, String methodName, int pos, SpelNodeImpl... arguments) {
@@ -99,21 +102,22 @@ public class MethodReference extends SpelNodeImpl {
state.popActiveContextObject();
}
}
+ List argumentTypes = getTypes(arguments);
if (currentContext.getValue() == null) {
if (this.nullSafe) {
return TypedValue.NULL;
}
else {
throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED,
- FormatHelper.formatMethodForMessage(this.name, getTypes(arguments)));
+ FormatHelper.formatMethodForMessage(this.name, argumentTypes));
}
}
- MethodExecutor executorToUse = this.cachedExecutor;
+ MethodExecutor executorToUse = getCachedExecutor(argumentTypes);
if (executorToUse != null) {
try {
- return executorToUse.execute(
- state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
+ return executorToUse.execute(state.getEvaluationContext(),
+ state.getActiveContextObject().getValue(), arguments);
}
catch (AccessException ae) {
// Two reasons this can occur:
@@ -134,11 +138,11 @@ public class MethodReference extends SpelNodeImpl {
}
// either there was no accessor or it no longer existed
- executorToUse = findAccessorForMethod(this.name, getTypes(arguments), state);
- this.cachedExecutor = executorToUse;
+ executorToUse = findAccessorForMethod(this.name, argumentTypes, state);
+ this.cachedExecutor = new CachedMethodExecutor(executorToUse, argumentTypes);
try {
- return executorToUse.execute(
- state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments);
+ return executorToUse.execute(state.getEvaluationContext(),
+ state.getActiveContextObject().getValue(), arguments);
}
catch (AccessException ae) {
// Same unwrapping exception handling as above in above catch block
@@ -158,12 +162,10 @@ public class MethodReference extends SpelNodeImpl {
if (rootCause instanceof RuntimeException) {
throw (RuntimeException) rootCause;
}
- else {
- throw new ExpressionInvocationTargetException(getStartPosition(),
- "A problem occurred when trying to execute method '" + this.name +
- "' on object of type '" + state.getActiveContextObject().getValue().getClass().getName() + "'",
- rootCause);
- }
+ throw new ExpressionInvocationTargetException(getStartPosition(),
+ "A problem occurred when trying to execute method '" + this.name +
+ "' on object of type '" + state.getActiveContextObject().getValue().getClass().getName() + "'",
+ rootCause);
}
}
@@ -172,7 +174,7 @@ public class MethodReference extends SpelNodeImpl {
for (Object argument : arguments) {
descriptors.add(TypeDescriptor.forObject(argument));
}
- return descriptors;
+ return Collections.unmodifiableList(descriptors);
}
@Override
@@ -180,42 +182,57 @@ public class MethodReference extends SpelNodeImpl {
StringBuilder sb = new StringBuilder();
sb.append(this.name).append("(");
for (int i = 0; i < getChildCount(); i++) {
- if (i > 0)
+ if (i > 0) {
sb.append(",");
+ }
sb.append(getChild(i).toStringAST());
}
sb.append(")");
return sb.toString();
}
- private MethodExecutor findAccessorForMethod(String name, List argumentTypes, ExpressionState state)
+ private MethodExecutor findAccessorForMethod(String name,
+ List argumentTypes, ExpressionState state)
throws SpelEvaluationException {
-
- return findAccessorForMethod(name,argumentTypes,state.getActiveContextObject().getValue(),state.getEvaluationContext());
+ return findAccessorForMethod(name, argumentTypes,
+ state.getActiveContextObject().getValue(), state.getEvaluationContext());
}
private MethodExecutor findAccessorForMethod(String name,
List argumentTypes, Object contextObject, EvaluationContext eContext)
throws SpelEvaluationException {
- List mResolvers = eContext.getMethodResolvers();
- if (mResolvers != null) {
- for (MethodResolver methodResolver : mResolvers) {
+ List methodResolvers = eContext.getMethodResolvers();
+ if (methodResolvers != null) {
+ for (MethodResolver methodResolver : methodResolvers) {
try {
- MethodExecutor cEx = methodResolver.resolve(eContext, contextObject, name, argumentTypes);
- if (cEx != null) {
- return cEx;
+ MethodExecutor methodExecutor = methodResolver.resolve(eContext,
+ contextObject, name, argumentTypes);
+ if (methodExecutor != null) {
+ return methodExecutor;
}
}
catch (AccessException ex) {
- throw new SpelEvaluationException(getStartPosition(),ex, SpelMessage.PROBLEM_LOCATING_METHOD,
- name, contextObject.getClass());
+ throw new SpelEvaluationException(getStartPosition(), ex,
+ SpelMessage.PROBLEM_LOCATING_METHOD, name,
+ contextObject.getClass());
}
}
}
- throw new SpelEvaluationException(getStartPosition(),SpelMessage.METHOD_NOT_FOUND,
+ throw new SpelEvaluationException(
+ getStartPosition(),
+ SpelMessage.METHOD_NOT_FOUND,
FormatHelper.formatMethodForMessage(name, argumentTypes),
- FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class>) contextObject) : contextObject.getClass()));
+ FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class>) contextObject)
+ : contextObject.getClass()));
+ }
+
+ private MethodExecutor getCachedExecutor(List argumentTypes) {
+ if (this.cachedExecutor == null || !this.cachedExecutor.isSuitable(argumentTypes)) {
+ this.cachedExecutor = null;
+ return null;
+ }
+ return this.cachedExecutor.get();
}
@@ -229,16 +246,21 @@ public class MethodReference extends SpelNodeImpl {
private final Object[] arguments;
+ private List argumentTypes;
+
+
MethodValueRef(ExpressionState state, EvaluationContext evaluationContext, Object object, Object[] arguments) {
this.state = state;
this.evaluationContext = evaluationContext;
this.target = object;
this.arguments = arguments;
+ this.argumentTypes = getTypes(this.arguments);
}
+
@Override
public TypedValue getValue() {
- MethodExecutor executorToUse = cachedExecutor;
+ MethodExecutor executorToUse = getCachedExecutor(this.argumentTypes);
if (executorToUse != null) {
try {
return executorToUse.execute(this.evaluationContext, this.target, this.arguments);
@@ -257,21 +279,23 @@ public class MethodReference extends SpelNodeImpl {
throwSimpleExceptionIfPossible(this.state, ae);
// at this point we know it wasn't a user problem so worth a retry if a better candidate can be found
- cachedExecutor = null;
+ MethodReference.this.cachedExecutor = null;
}
}
// either there was no accessor or it no longer existed
- executorToUse = findAccessorForMethod(name, getTypes(this.arguments), this.target, this.evaluationContext);
- cachedExecutor = executorToUse;
+ executorToUse = findAccessorForMethod(MethodReference.this.name, argumentTypes, this.target, this.evaluationContext);
+ MethodReference.this.cachedExecutor = new CachedMethodExecutor(executorToUse, this.argumentTypes);
try {
return executorToUse.execute(this.evaluationContext, this.target, this.arguments);
}
- catch (AccessException ae) {
+ catch (AccessException ex) {
// Same unwrapping exception handling as above in above catch block
- throwSimpleExceptionIfPossible(this.state, ae);
- throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION,
- name, this.state.getActiveContextObject().getValue().getClass().getName(), ae.getMessage());
+ throwSimpleExceptionIfPossible(this.state, ex);
+ throw new SpelEvaluationException(getStartPosition(), ex,
+ SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION,
+ MethodReference.this.name, this.state.getActiveContextObject().getValue().getClass().getName(),
+ ex.getMessage());
}
}
@@ -286,4 +310,27 @@ public class MethodReference extends SpelNodeImpl {
}
}
+
+ private static class CachedMethodExecutor {
+
+ private final MethodExecutor methodExecutor;
+
+ private final List argumentTypes;
+
+
+ public CachedMethodExecutor(MethodExecutor methodExecutor,
+ List argumentTypes) {
+ this.methodExecutor = methodExecutor;
+ this.argumentTypes = argumentTypes;
+ }
+
+
+ public boolean isSuitable(List argumentTypes) {
+ return (this.methodExecutor != null && this.argumentTypes.equals(argumentTypes));
+ }
+
+ public MethodExecutor get() {
+ return this.methodExecutor;
+ }
+ }
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java
index 7ec4c6105a..5f7beb80f8 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
/**
+ * Expression language AST node that represents null.
+ *
* @author Andy Clement
* @since 3.0
*/
@@ -28,6 +30,7 @@ public class NullLiteral extends Literal {
super(null,pos);
}
+
@Override
public TypedValue getLiteralValue() {
return TypedValue.NULL;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java
index b2140d11e0..c6c69df23f 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpAnd.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ public class OpAnd extends Operator {
super("and", pos, operands);
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
if (getBooleanValue(state, getLeftOperand()) == false) {
@@ -52,9 +53,9 @@ public class OpAnd extends Operator {
assertValueNotNull(value);
return value;
}
- catch (SpelEvaluationException ee) {
- ee.setPosition(operand.getStartPosition());
- throw ee;
+ catch (SpelEvaluationException ex) {
+ ex.setPosition(operand.getStartPosition());
+ throw ex;
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java
index 7536ddfa2a..0a5a1236d7 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDec.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,8 @@ import org.springframework.util.Assert;
*/
public class OpDec extends Operator {
- private boolean postfix; // false means prefix
+ private final boolean postfix; // false means prefix
+
public OpDec(int pos, boolean postfix, SpelNodeImpl... operands) {
super("--", pos, operands);
@@ -41,6 +42,7 @@ public class OpDec extends Operator {
this.postfix = postfix;
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl operand = getLeftOperand();
@@ -57,26 +59,37 @@ public class OpDec extends Operator {
if (operandValue instanceof Number) {
Number op1 = (Number) operandValue;
if (op1 instanceof Double) {
- newValue = new TypedValue(op1.doubleValue() - 1.0d, operandTypedValue.getTypeDescriptor());
- } else if (op1 instanceof Float) {
- newValue = new TypedValue(op1.floatValue() - 1.0f, operandTypedValue.getTypeDescriptor());
- } else if (op1 instanceof Long) {
- newValue = new TypedValue(op1.longValue() - 1L, operandTypedValue.getTypeDescriptor());
- } else if (op1 instanceof Short) {
- newValue = new TypedValue(op1.shortValue() - (short)1, operandTypedValue.getTypeDescriptor());
- } else {
- newValue = new TypedValue(op1.intValue() - 1, operandTypedValue.getTypeDescriptor());
+ newValue = new TypedValue(op1.doubleValue() - 1.0d,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else if (op1 instanceof Float) {
+ newValue = new TypedValue(op1.floatValue() - 1.0f,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else if (op1 instanceof Long) {
+ newValue = new TypedValue(op1.longValue() - 1L,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else if (op1 instanceof Short) {
+ newValue = new TypedValue(op1.shortValue() - (short) 1,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else {
+ newValue = new TypedValue(op1.intValue() - 1,
+ operandTypedValue.getTypeDescriptor());
}
}
if (newValue==null) {
try {
newValue = state.operate(Operation.SUBTRACT, returnValue.getValue(), 1);
- } catch (SpelEvaluationException see) {
- if (see.getMessageCode()==SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
+ }
+ catch (SpelEvaluationException ex) {
+ if (ex.getMessageCode() == SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
// This means the operand is not decrementable
throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_DECREMENTABLE,operand.toStringAST());
- } else {
- throw see;
+ }
+ else {
+ throw ex;
}
}
}
@@ -84,16 +97,19 @@ public class OpDec extends Operator {
// set the new value
try {
lvalue.setValue(newValue.getValue());
- } catch (SpelEvaluationException see) {
+ }
+ catch (SpelEvaluationException see) {
// if unable to set the value the operand is not writable (e.g. 1-- )
- if (see.getMessageCode()==SpelMessage.SETVALUE_NOT_SUPPORTED) {
- throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_DECREMENTABLE);
- } else {
+ if (see.getMessageCode() == SpelMessage.SETVALUE_NOT_SUPPORTED) {
+ throw new SpelEvaluationException(operand.getStartPosition(),
+ SpelMessage.OPERAND_NOT_DECREMENTABLE);
+ }
+ else {
throw see;
}
}
- if (!postfix) {
+ if (!this.postfix) {
// the return value is the new value, not the original value
returnValue = newValue;
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
index ca8130dbff..844e2aa1d8 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpDivide.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ public class OpDivide extends Operator {
super("/", pos, operands);
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
@@ -43,9 +44,11 @@ public class OpDivide extends Operator {
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() / op2.doubleValue());
- } else if (op1 instanceof Float || op2 instanceof Float) {
+ }
+ else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() / op2.floatValue());
- } else if (op1 instanceof Long || op2 instanceof Long) {
+ }
+ else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() / op2.longValue());
}
else {
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java
index 17f9eb8faf..1121cc840a 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,10 @@ public class OpEQ extends Operator {
super("==", pos, operands);
}
+
@Override
- public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
+ public BooleanTypedValue getValueInternal(ExpressionState state)
+ throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
@@ -41,18 +43,23 @@ public class OpEQ extends Operator {
Number op2 = (Number) right;
if (op1 instanceof Double || op2 instanceof Double) {
return BooleanTypedValue.forValue(op1.doubleValue() == op2.doubleValue());
- } else if (op1 instanceof Float || op2 instanceof Float) {
- return BooleanTypedValue.forValue(op1.floatValue() == op2.floatValue());
- } else if (op1 instanceof Long || op2 instanceof Long) {
+ }
+ else if (op1 instanceof Float || op2 instanceof Float) {
+ return BooleanTypedValue.forValue(op1.floatValue() == op2.floatValue());
+ }
+ else if (op1 instanceof Long || op2 instanceof Long) {
return BooleanTypedValue.forValue(op1.longValue() == op2.longValue());
- } else {
+ }
+ else {
return BooleanTypedValue.forValue(op1.intValue() == op2.intValue());
}
}
- if (left!=null && (left instanceof Comparable)) {
- return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) == 0);
- } else {
- return BooleanTypedValue.forValue(left==right);
+ if (left != null && (left instanceof Comparable)) {
+ return BooleanTypedValue.forValue(state.getTypeComparator().compare(left,
+ right) == 0);
+ }
+ else {
+ return BooleanTypedValue.forValue(left == right);
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java
index a267b7037e..6e1294e461 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGE.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ public class OpGE extends Operator {
super(">=", pos, operands);
}
+
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
@@ -40,11 +41,14 @@ public class OpGE extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() >= rightNumber.doubleValue());
- } else if (leftNumber instanceof Float || rightNumber instanceof Float) {
+ }
+ else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() >= rightNumber.floatValue());
- } else if (leftNumber instanceof Long || rightNumber instanceof Long) {
- return BooleanTypedValue.forValue( leftNumber.longValue() >= rightNumber.longValue());
- } else {
+ }
+ else if (leftNumber instanceof Long || rightNumber instanceof Long) {
+ return BooleanTypedValue.forValue(leftNumber.longValue() >= rightNumber.longValue());
+ }
+ else {
return BooleanTypedValue.forValue(leftNumber.intValue() >= rightNumber.intValue());
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java
index e2b088a2f5..3d751c976d 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpGT.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +32,7 @@ public class OpGT extends Operator {
super(">", pos, operands);
}
+
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
@@ -41,9 +42,11 @@ public class OpGT extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() > rightNumber.doubleValue());
- } else if (leftNumber instanceof Long || rightNumber instanceof Long) {
+ }
+ else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() > rightNumber.longValue());
- } else {
+ }
+ else {
return BooleanTypedValue.forValue(leftNumber.intValue() > rightNumber.intValue());
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java
index eb829e608a..43937ca953 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpInc.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,7 +33,8 @@ import org.springframework.util.Assert;
*/
public class OpInc extends Operator {
- private boolean postfix; // false means prefix
+ private final boolean postfix; // false means prefix
+
public OpInc(int pos, boolean postfix, SpelNodeImpl... operands) {
super("++", pos, operands);
@@ -41,6 +42,7 @@ public class OpInc extends Operator {
this.postfix = postfix;
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl operand = getLeftOperand();
@@ -55,43 +57,56 @@ public class OpInc extends Operator {
if (operandValue instanceof Number) {
Number op1 = (Number) operandValue;
if (op1 instanceof Double) {
- newValue = new TypedValue(op1.doubleValue() + 1.0d, operandTypedValue.getTypeDescriptor());
- } else if (op1 instanceof Float) {
- newValue = new TypedValue(op1.floatValue() + 1.0f, operandTypedValue.getTypeDescriptor());
- } else if (op1 instanceof Long) {
- newValue = new TypedValue(op1.longValue() + 1L, operandTypedValue.getTypeDescriptor());
- } else if (op1 instanceof Short) {
- newValue = new TypedValue(op1.shortValue() + (short)1, operandTypedValue.getTypeDescriptor());
- } else {
- newValue = new TypedValue(op1.intValue() + 1, operandTypedValue.getTypeDescriptor());
+ newValue = new TypedValue(op1.doubleValue() + 1.0d,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else if (op1 instanceof Float) {
+ newValue = new TypedValue(op1.floatValue() + 1.0f,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else if (op1 instanceof Long) {
+ newValue = new TypedValue(op1.longValue() + 1L,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else if (op1 instanceof Short) {
+ newValue = new TypedValue(op1.shortValue() + (short) 1,
+ operandTypedValue.getTypeDescriptor());
+ }
+ else {
+ newValue = new TypedValue(op1.intValue() + 1,
+ operandTypedValue.getTypeDescriptor());
}
}
- if (newValue==null) {
+ if (newValue == null) {
try {
newValue = state.operate(Operation.ADD, returnValue.getValue(), 1);
- } catch (SpelEvaluationException see) {
- if (see.getMessageCode()==SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
+ }
+ catch (SpelEvaluationException ex) {
+ if (ex.getMessageCode() == SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES) {
// This means the operand is not incrementable
- throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_INCREMENTABLE,operand.toStringAST());
- } else {
- throw see;
+ throw new SpelEvaluationException(operand.getStartPosition(),
+ SpelMessage.OPERAND_NOT_INCREMENTABLE, operand.toStringAST());
}
+ throw ex;
}
}
// set the name value
try {
lvalue.setValue(newValue.getValue());
- } catch (SpelEvaluationException see) {
+ }
+ catch (SpelEvaluationException see) {
// if unable to set the value the operand is not writable (e.g. 1++ )
- if (see.getMessageCode()==SpelMessage.SETVALUE_NOT_SUPPORTED) {
- throw new SpelEvaluationException(operand.getStartPosition(),SpelMessage.OPERAND_NOT_INCREMENTABLE);
- } else {
+ if (see.getMessageCode() == SpelMessage.SETVALUE_NOT_SUPPORTED) {
+ throw new SpelEvaluationException(operand.getStartPosition(),
+ SpelMessage.OPERAND_NOT_INCREMENTABLE);
+ }
+ else {
throw see;
}
}
- if (!postfix) {
+ if (!this.postfix) {
// the return value is the new value, not the original value
returnValue = newValue;
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java
index a09c7c7fb7..47424db204 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLE.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,10 @@ public class OpLE extends Operator {
super("<=", pos, operands);
}
+
@Override
- public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
+ public BooleanTypedValue getValueInternal(ExpressionState state)
+ throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
if (left instanceof Number && right instanceof Number) {
@@ -41,15 +43,18 @@ public class OpLE extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() <= rightNumber.doubleValue());
- } else if (leftNumber instanceof Float || rightNumber instanceof Float) {
+ }
+ else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() <= rightNumber.floatValue());
- } else if (leftNumber instanceof Long || rightNumber instanceof Long) {
+ }
+ else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() <= rightNumber.longValue());
- } else {
+ }
+ else {
return BooleanTypedValue.forValue(leftNumber.intValue() <= rightNumber.intValue());
}
}
- return BooleanTypedValue.forValue( state.getTypeComparator().compare(left, right) <= 0);
+ return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) <= 0);
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java
index 0f0e12d045..dc8916dda8 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpLT.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,10 @@ public class OpLT extends Operator {
super("<", pos, operands);
}
+
@Override
- public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
+ public BooleanTypedValue getValueInternal(ExpressionState state)
+ throws EvaluationException {
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
// TODO could leave all of these to the comparator - just seems quicker to do some here
@@ -42,11 +44,14 @@ public class OpLT extends Operator {
Number rightNumber = (Number) right;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
return BooleanTypedValue.forValue(leftNumber.doubleValue() < rightNumber.doubleValue());
- } else if (leftNumber instanceof Float || rightNumber instanceof Float) {
+ }
+ else if (leftNumber instanceof Float || rightNumber instanceof Float) {
return BooleanTypedValue.forValue(leftNumber.floatValue() < rightNumber.floatValue());
- } else if (leftNumber instanceof Long || rightNumber instanceof Long) {
+ }
+ else if (leftNumber instanceof Long || rightNumber instanceof Long) {
return BooleanTypedValue.forValue(leftNumber.longValue() < rightNumber.longValue());
- } else {
+ }
+ else {
return BooleanTypedValue.forValue(leftNumber.intValue() < rightNumber.intValue());
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java
index 1afb3b28c4..143dfd9014 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMinus.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,11 +27,12 @@ import org.springframework.expression.spel.ExpressionState;
* subtraction of doubles (floats are represented as doubles)
* subtraction of longs
* subtraction of integers
- * subtraction of an int from a string of one character (effectively decreasing that character), so 'd'-3='a'
+ * subtraction of an int from a string of one character (effectively decreasing that
+ * character), so 'd'-3='a'
*
- * It can be used as a unary operator for numbers (double/long/int). The standard promotions are performed
- * when the operand types vary (double-int=double).
- * For other options it defers to the registered overloader.
+ * It can be used as a unary operator for numbers (double/long/int). The standard
+ * promotions are performed when the operand types vary (double-int=double). For other
+ * options it defers to the registered overloader.
*
* @author Andy Clement
* @since 3.0
@@ -44,46 +45,61 @@ public class OpMinus extends Operator {
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
+
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
+
if (rightOp == null) {// If only one operand, then this is unary minus
Object operand = leftOp.getValueInternal(state).getValue();
if (operand instanceof Number) {
Number n = (Number) operand;
if (operand instanceof Double) {
return new TypedValue(0 - n.doubleValue());
- } else if (operand instanceof Float) {
+ }
+
+ if (operand instanceof Float) {
return new TypedValue(0 - n.floatValue());
- } else if (operand instanceof Long) {
+ }
+
+ if (operand instanceof Long) {
return new TypedValue(0 - n.longValue());
- } else {
- return new TypedValue(0 - n.intValue());
}
+ return new TypedValue(0 - n.intValue());
}
+
return state.operate(Operation.SUBTRACT, operand, null);
- } else {
- Object left = leftOp.getValueInternal(state).getValue();
- Object right = rightOp.getValueInternal(state).getValue();
- if (left instanceof Number && right instanceof Number) {
- Number op1 = (Number) left;
- Number op2 = (Number) right;
- if (op1 instanceof Double || op2 instanceof Double) {
- return new TypedValue(op1.doubleValue() - op2.doubleValue());
- } else if (op1 instanceof Float || op2 instanceof Float) {
- return new TypedValue(op1.floatValue() - op2.floatValue());
- } else if (op1 instanceof Long || op2 instanceof Long) {
- return new TypedValue(op1.longValue() - op2.longValue());
- } else {
- return new TypedValue(op1.intValue() - op2.intValue());
- }
- } else if (left instanceof String && right instanceof Integer && ((String)left).length()==1) {
- String theString = (String) left;
- Integer theInteger = (Integer) right;
- // implements character - int (ie. b - 1 = a)
- return new TypedValue(Character.toString((char) (theString.charAt(0) - theInteger)));
- }
- return state.operate(Operation.SUBTRACT, left, right);
}
+
+ Object left = leftOp.getValueInternal(state).getValue();
+ Object right = rightOp.getValueInternal(state).getValue();
+
+ if (left instanceof Number && right instanceof Number) {
+ Number op1 = (Number) left;
+ Number op2 = (Number) right;
+ if (op1 instanceof Double || op2 instanceof Double) {
+ return new TypedValue(op1.doubleValue() - op2.doubleValue());
+ }
+
+ if (op1 instanceof Float || op2 instanceof Float) {
+ return new TypedValue(op1.floatValue() - op2.floatValue());
+ }
+
+ if (op1 instanceof Long || op2 instanceof Long) {
+ return new TypedValue(op1.longValue() - op2.longValue());
+ }
+
+ return new TypedValue(op1.intValue() - op2.intValue());
+ }
+ else if (left instanceof String && right instanceof Integer
+ && ((String) left).length() == 1) {
+ String theString = (String) left;
+ Integer theInteger = (Integer) right;
+ // implements character - int (ie. b - 1 = a)
+ return new TypedValue(Character.toString((char)
+ (theString.charAt(0) - theInteger)));
+ }
+
+ return state.operate(Operation.SUBTRACT, left, right);
}
@Override
@@ -95,8 +111,8 @@ public class OpMinus extends Operator {
}
@Override
public SpelNodeImpl getRightOperand() {
- if (children.length<2) {return null;}
- return children[1];
+ if (this.children.length<2) {return null;}
+ return this.children[1];
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java
index 559c43b55e..48b511229d 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpModulus.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ public class OpModulus extends Operator {
super("%", pos, operands);
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
@@ -42,11 +43,14 @@ public class OpModulus extends Operator {
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
return new TypedValue(op1.doubleValue() % op2.doubleValue());
- } else if (op1 instanceof Float || op2 instanceof Float) {
+ }
+ else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(op1.floatValue() % op2.floatValue());
- } else if (op1 instanceof Long || op2 instanceof Long) {
+ }
+ else if (op1 instanceof Long || op2 instanceof Long) {
return new TypedValue(op1.longValue() % op2.longValue());
- } else {
+ }
+ else {
return new TypedValue(op1.intValue() % op2.intValue());
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java
index 42b0259416..e795dff8b8 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpMultiply.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,10 +25,11 @@ import org.springframework.expression.spel.ExpressionState;
* Implements the {@code multiply} operator.
*
* Conversions and promotions are handled as defined in
- * Section 5.6.2
- * of the Java Language Specification :
+ * Section
+ * 5.6.2 of the Java Language Specification :
*
- *
If any of the operands is of a reference type, unboxing conversion (Section 5.1.8) is performed. Then:
+ *
If any of the operands is of a reference type, unboxing conversion (Section 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.
@@ -44,6 +45,7 @@ public class OpMultiply extends Operator {
super("*", pos, operands);
}
+
/**
* Implements the {@code multiply} operator directly here for certain types
* of supported operands and otherwise delegates to any registered overloader
@@ -58,23 +60,27 @@ public class OpMultiply extends Operator {
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
+
Object operandOne = getLeftOperand().getValueInternal(state).getValue();
Object operandTwo = getRightOperand().getValueInternal(state).getValue();
+
if (operandOne instanceof Number && operandTwo instanceof Number) {
Number leftNumber = (Number) operandOne;
Number rightNumber = (Number) operandTwo;
if (leftNumber instanceof Double || rightNumber instanceof Double) {
- return new TypedValue(leftNumber.doubleValue() * rightNumber.doubleValue());
+ return new TypedValue(leftNumber.doubleValue()
+ * rightNumber.doubleValue());
}
- else if (leftNumber instanceof Float || rightNumber instanceof Float) {
+
+ if (leftNumber instanceof Float || rightNumber instanceof Float) {
return new TypedValue(leftNumber.floatValue() * rightNumber.floatValue());
}
- else if (leftNumber instanceof Long || rightNumber instanceof Long) {
+
+ if (leftNumber instanceof Long || rightNumber instanceof Long) {
return new TypedValue(leftNumber.longValue() * rightNumber.longValue());
}
- else {
- return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
- }
+
+ return new TypedValue(leftNumber.intValue() * rightNumber.intValue());
}
else if (operandOne instanceof String && operandTwo instanceof Integer) {
int repeats = (Integer) operandTwo;
@@ -84,6 +90,7 @@ public class OpMultiply extends Operator {
}
return new TypedValue(result.toString());
}
+
return state.operate(Operation.MULTIPLY, operandOne, operandTwo);
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java
index 558ccb9763..fdf65c24d7 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,29 +32,38 @@ public class OpNE extends Operator {
super("!=", pos, operands);
}
+
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
+
Object left = getLeftOperand().getValueInternal(state).getValue();
Object right = getRightOperand().getValueInternal(state).getValue();
+
if (left instanceof Number && right instanceof Number) {
Number op1 = (Number) left;
Number op2 = (Number) right;
+
if (op1 instanceof Double || op2 instanceof Double) {
return BooleanTypedValue.forValue(op1.doubleValue() != op2.doubleValue());
- } else if (op1 instanceof Float || op2 instanceof Float) {
- return BooleanTypedValue.forValue(op1.floatValue() != op2.floatValue());
- } else if (op1 instanceof Long || op2 instanceof Long) {
- return BooleanTypedValue.forValue(op1.longValue() != op2.longValue());
- } else {
- return BooleanTypedValue.forValue(op1.intValue() != op2.intValue());
}
+
+ if (op1 instanceof Float || op2 instanceof Float) {
+ return BooleanTypedValue.forValue(op1.floatValue() != op2.floatValue());
+ }
+
+ if (op1 instanceof Long || op2 instanceof Long) {
+ return BooleanTypedValue.forValue(op1.longValue() != op2.longValue());
+ }
+
+ return BooleanTypedValue.forValue(op1.intValue() != op2.intValue());
}
- if (left!=null && (left instanceof Comparable)) {
- return BooleanTypedValue.forValue(state.getTypeComparator().compare(left, right) != 0);
- } else {
- return BooleanTypedValue.forValue(left!=right);
+ if (left != null && (left instanceof Comparable)) {
+ return BooleanTypedValue.forValue(state.getTypeComparator().compare(left,
+ right) != 0);
}
+
+ return BooleanTypedValue.forValue(left != right);
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java
index c4d3cd4cf4..ce21a1cb9a 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpOr.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@ public class OpOr extends Operator {
super("or", pos, operands);
}
+
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
if (getBooleanValue(state, getLeftOperand())) {
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java
index 7443dd141c..53dad0ff2b 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpPlus.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,9 @@ import org.springframework.util.Assert;
*
add integers
* concatenate strings
*
- * It can be used as a unary operator for numbers (double/long/int). The standard promotions are performed
- * when the operand types vary (double+int=double). For other options it defers to the registered overloader.
+ * It can be used as a unary operator for numbers (double/long/int). The standard
+ * promotions are performed when the operand types vary (double+int=double). For other
+ * options it defers to the registered overloader.
*
* @author Andy Clement
* @author Ivo Smid
@@ -46,72 +47,86 @@ public class OpPlus extends Operator {
Assert.notEmpty(operands);
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
SpelNodeImpl rightOp = getRightOperand();
+
if (rightOp == null) { // If only one operand, then this is unary plus
Object operandOne = leftOp.getValueInternal(state).getValue();
if (operandOne instanceof Number) {
if (operandOne instanceof Double || operandOne instanceof Long) {
return new TypedValue(operandOne);
- } else if (operandOne instanceof Float) {
- return new TypedValue(((Number) operandOne).floatValue());
- } else {
- return new TypedValue(((Number) operandOne).intValue());
}
+ if (operandOne instanceof Float) {
+ return new TypedValue(((Number) operandOne).floatValue());
+ }
+ return new TypedValue(((Number) operandOne).intValue());
}
return state.operate(Operation.ADD, operandOne, null);
}
- else {
- final TypedValue operandOneValue = leftOp.getValueInternal(state);
- final Object operandOne = operandOneValue.getValue();
- final TypedValue operandTwoValue = rightOp.getValueInternal(state);
- final Object operandTwo = operandTwoValue.getValue();
+ final TypedValue operandOneValue = leftOp.getValueInternal(state);
+ final Object operandOne = operandOneValue.getValue();
- if (operandOne instanceof Number && operandTwo instanceof Number) {
- Number op1 = (Number) operandOne;
- Number op2 = (Number) operandTwo;
- if (op1 instanceof Double || op2 instanceof Double) {
- return new TypedValue(op1.doubleValue() + op2.doubleValue());
- } else if (op1 instanceof Float || op2 instanceof Float) {
- return new TypedValue(op1.floatValue() + op2.floatValue());
- } else if (op1 instanceof Long || op2 instanceof Long) {
- return new TypedValue(op1.longValue() + op2.longValue());
- } else { // TODO what about overflow?
- return new TypedValue(op1.intValue() + op2.intValue());
- }
- } else if (operandOne instanceof String && operandTwo instanceof String) {
- return new TypedValue(new StringBuilder((String) operandOne).append((String) operandTwo).toString());
- } else if (operandOne instanceof String) {
- StringBuilder result = new StringBuilder((String) operandOne);
- result.append((operandTwo == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
- return new TypedValue(result.toString());
- } else if (operandTwo instanceof String) {
- StringBuilder result = new StringBuilder((operandOne == null ? "null" : convertTypedValueToString(
- operandOneValue, state)));
- result.append((String) operandTwo);
- return new TypedValue(result.toString());
+ final TypedValue operandTwoValue = rightOp.getValueInternal(state);
+ final Object operandTwo = operandTwoValue.getValue();
+
+ if (operandOne instanceof Number && operandTwo instanceof Number) {
+ Number op1 = (Number) operandOne;
+ Number op2 = (Number) operandTwo;
+ if (op1 instanceof Double || op2 instanceof Double) {
+ return new TypedValue(op1.doubleValue() + op2.doubleValue());
}
- return state.operate(Operation.ADD, operandOne, operandTwo);
+ if (op1 instanceof Float || op2 instanceof Float) {
+ return new TypedValue(op1.floatValue() + op2.floatValue());
+ }
+ if (op1 instanceof Long || op2 instanceof Long) {
+ return new TypedValue(op1.longValue() + op2.longValue());
+ }
+ // TODO what about overflow?
+ return new TypedValue(op1.intValue() + op2.intValue());
}
+
+ if (operandOne instanceof String && operandTwo instanceof String) {
+ return new TypedValue(new StringBuilder((String) operandOne).append(
+ (String) operandTwo).toString());
+ }
+
+ if (operandOne instanceof String) {
+ StringBuilder result = new StringBuilder((String) operandOne);
+ result.append((operandTwo == null ? "null" : convertTypedValueToString(
+ operandTwoValue, state)));
+ return new TypedValue(result.toString());
+ }
+
+ if (operandTwo instanceof String) {
+ StringBuilder result = new StringBuilder((operandOne == null ? "null"
+ : convertTypedValueToString(operandOneValue, state)));
+ result.append((String) operandTwo);
+ return new TypedValue(result.toString());
+ }
+
+ return state.operate(Operation.ADD, operandOne, operandTwo);
}
@Override
public String toStringAST() {
- if (children.length<2) { // unary plus
+ if (this.children.length<2) { // unary plus
return new StringBuilder().append("+").append(getLeftOperand().toStringAST()).toString();
}
+
return super.toStringAST();
}
@Override
public SpelNodeImpl getRightOperand() {
- if (children.length < 2) {
+ if (this.children.length < 2) {
return null;
}
- return children[1];
+
+ return this.children[1];
}
/**
@@ -127,11 +142,12 @@ public class OpPlus extends Operator {
final TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(String.class);
if (typeConverter.canConvert(value.getTypeDescriptor(), typeDescriptor)) {
- final Object obj = typeConverter.convertValue(value.getValue(), value.getTypeDescriptor(), typeDescriptor);
+ final Object obj = typeConverter.convertValue(value.getValue(),
+ value.getTypeDescriptor(), typeDescriptor);
return String.valueOf(obj);
- } else {
- return String.valueOf(value.getValue());
}
+
+ return String.valueOf(value.getValue());
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java
index 863f1a218f..637ba71185 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,10 +16,10 @@
package org.springframework.expression.spel.ast;
-
/**
- * Common supertype for operators that operate on either one or two operands. In the case of multiply or divide there
- * would be two operands, but for unary plus or minus, there is only one.
+ * Common supertype for operators that operate on either one or two operands. In the case
+ * of multiply or divide there would be two operands, but for unary plus or minus, there
+ * is only one.
*
* @author Andy Clement
* @since 3.0
@@ -28,21 +28,23 @@ public abstract class Operator extends SpelNodeImpl {
String operatorName;
+
public Operator(String payload,int pos,SpelNodeImpl... operands) {
super(pos, operands);
this.operatorName = payload;
}
+
public SpelNodeImpl getLeftOperand() {
- return children[0];
+ return this.children[0];
}
public SpelNodeImpl getRightOperand() {
- return children[1];
+ return this.children[1];
}
public final String getOperatorName() {
- return operatorName;
+ return this.operatorName;
}
/**
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java
index d18695275e..7f38830d7a 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,9 +26,10 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
- * Represents the between operator. The left operand to between must be a single value and the right operand must be a
- * list - this operator returns true if the left operand is between (using the registered comparator) the two elements
- * in the list. The definition of between being inclusive follows the SQL BETWEEN definition.
+ * Represents the between operator. The left operand to between must be a single value and
+ * the right operand must be a list - this operator returns true if the left operand is
+ * between (using the registered comparator) the two elements in the list. The definition
+ * of between being inclusive follows the SQL BETWEEN definition.
*
* @author Andy Clement
* @since 3.0
@@ -40,8 +41,9 @@ public class OperatorBetween extends Operator {
}
/**
- * Returns a boolean based on whether a value is in the range expressed. The first operand is any value whilst the
- * second is a list of two values - those two values being the bounds allowed for the first operand (inclusive).
+ * Returns a boolean based on whether a value is in the range expressed. The first
+ * operand is any value whilst the second is a list of two values - those two values
+ * being the bounds allowed for the first operand (inclusive).
* @param state the expression state
* @return true if the left operand is in the range specified, false otherwise
* @throws EvaluationException if there is a problem evaluating the expression
@@ -59,8 +61,10 @@ public class OperatorBetween extends Operator {
Object high = l.get(1);
TypeComparator comparator = state.getTypeComparator();
try {
- return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 && comparator.compare(left, high) <= 0));
- } catch (SpelEvaluationException ex) {
+ return BooleanTypedValue.forValue((comparator.compare(left, low) >= 0 &&
+ comparator.compare(left, high) <= 0));
+ }
+ catch (SpelEvaluationException ex) {
ex.setPosition(getStartPosition());
throw ex;
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java
index bf7d0edc91..2f50e86c82 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,8 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
- * The operator 'instanceof' checks if an object is of the class specified in the right hand operand,
- * in the same way that {@code instanceof} does in Java.
+ * The operator 'instanceof' checks if an object is of the class specified in the right
+ * hand operand, in the same way that {@code instanceof} does in Java.
*
* @author Andy Clement
* @since 3.0
@@ -36,11 +36,13 @@ public class OperatorInstanceof extends Operator {
super("instanceof", pos, operands);
}
+
/**
- * Compare the left operand to see it is an instance of the type specified as the right operand.
- * The right operand must be a class.
+ * Compare the left operand to see it is an instance of the type specified as the
+ * right operand. The right operand must be a class.
* @param state the expression state
- * @return true if the left operand is an instanceof of the right operand, otherwise false
+ * @return true if the left operand is an instanceof of the right operand, otherwise
+ * false
* @throws EvaluationException if there is a problem evaluating the expression
*/
@Override
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java
index 24b7ea46de..573c80de42 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,8 +27,9 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.BooleanTypedValue;
/**
- * Implements the matches operator. Matches takes two operands. The first is a string and the second is a java regex. It
- * will return true when getValue() is called if the first operand matches the regex.
+ * Implements the matches operator. Matches takes two operands. The first is a string and
+ * the second is a java regex. It will return true when getValue() is called if the first
+ * operand matches the regex.
*
* @author Andy Clement
* @since 3.0
@@ -39,11 +40,14 @@ public class OperatorMatches extends Operator {
super("matches", pos, operands);
}
+
/**
* Check the first operand matches the regex specified as the second operand.
* @param state the expression state
- * @return true if the first operand matches the regex specified as the second operand, otherwise false
- * @throws EvaluationException if there is a problem evaluating the expression (e.g. the regex is invalid)
+ * @return true if the first operand matches the regex specified as the second
+ * operand, otherwise false
+ * @throws EvaluationException if there is a problem evaluating the expression (e.g.
+ * the regex is invalid)
*/
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java
index 33ae9e0d97..3e5be7217a 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,18 +36,19 @@ public class OperatorNot extends SpelNodeImpl { // Not is a unary operator so do
super(pos, operand);
}
+
@Override
public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException {
try {
- Boolean value = children[0].getValue(state, Boolean.class);
+ Boolean value = this.children[0].getValue(state, Boolean.class);
if (value == null) {
throw new SpelEvaluationException(SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
}
return BooleanTypedValue.forValue(!value);
}
- catch (SpelEvaluationException see) {
- see.setPosition(getChild(0).getStartPosition());
- throw see;
+ catch (SpelEvaluationException ex) {
+ ex.setPosition(getChild(0).getStartPosition());
+ throw ex;
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java
index 0c41e6a8dc..f05e1b7a88 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorPower.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ public class OperatorPower extends Operator {
super("^", pos, operands);
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
SpelNodeImpl leftOp = getLeftOperand();
@@ -44,18 +45,22 @@ public class OperatorPower extends Operator {
Number op1 = (Number) operandOne;
Number op2 = (Number) operandTwo;
if (op1 instanceof Double || op2 instanceof Double) {
- return new TypedValue(Math.pow(op1.doubleValue(),op2.doubleValue()));
- } else if (op1 instanceof Float || op2 instanceof Float) {
+ return new TypedValue(Math.pow(op1.doubleValue(), op2.doubleValue()));
+ }
+ else if (op1 instanceof Float || op2 instanceof Float) {
return new TypedValue(Math.pow(op1.floatValue(), op2.floatValue()));
- } else if (op1 instanceof Long || op2 instanceof Long) {
- double d= Math.pow(op1.longValue(), op2.longValue());
- return new TypedValue((long)d);
- } else {
- double d= Math.pow(op1.longValue(), op2.longValue());
+ }
+ else if (op1 instanceof Long || op2 instanceof Long) {
+ double d = Math.pow(op1.longValue(), op2.longValue());
+ return new TypedValue((long) d);
+ }
+ else {
+ double d = Math.pow(op1.longValue(), op2.longValue());
if (d > Integer.MAX_VALUE) {
- return new TypedValue((long)d);
- } else {
- return new TypedValue((int)d);
+ return new TypedValue((long) d);
+ }
+ else {
+ return new TypedValue((int) d);
}
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java
index 226063cf7c..d7fcb505d1 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Projection.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,8 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
- * Represents projection, where a given operation is performed on all elements in some input sequence, returning
- * a new sequence of the same size. For example:
+ * Represents projection, where a given operation is performed on all elements in some
+ * input sequence, returning a new sequence of the same size. For example:
* "{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}" returns "[n, y, n, y, n, y, n, y, n, y]"
*
* @author Andy Clement
@@ -49,6 +49,7 @@ public class Projection extends SpelNodeImpl {
this.nullSafe = nullSafe;
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
return getValueRef(state).getValue();
@@ -81,7 +82,8 @@ public class Projection extends SpelNodeImpl {
}
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this); // TODO unable to build correct type descriptor
}
- else if (operand instanceof Collection || operandIsArray) {
+
+ if (operand instanceof Collection || operandIsArray) {
Collection> data = (operand instanceof Collection ? (Collection>) operand :
Arrays.asList(ObjectUtils.toObjectArray(operand)));
List result = new ArrayList();
@@ -91,7 +93,7 @@ public class Projection extends SpelNodeImpl {
try {
state.pushActiveContextObject(new TypedValue(element));
state.enterScope("index", idx);
- Object value = children[0].getValueInternal(state).getValue();
+ Object value = this.children[0].getValueInternal(state).getValue();
if (value != null && operandIsArray) {
arrayElementType = determineCommonType(arrayElementType, value.getClass());
}
@@ -113,21 +115,17 @@ public class Projection extends SpelNodeImpl {
}
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
}
- else {
- if (operand==null) {
- if (this.nullSafe) {
- return ValueRef.NullValueRef.instance;
- }
- else {
- throw new SpelEvaluationException(getStartPosition(),
- SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null");
- }
- }
- else {
- throw new SpelEvaluationException(getStartPosition(),
- SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
+
+ if (operand==null) {
+ if (this.nullSafe) {
+ return ValueRef.NullValueRef.instance;
}
+ throw new SpelEvaluationException(getStartPosition(),
+ SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, "null");
}
+
+ throw new SpelEvaluationException(getStartPosition(),
+ SpelMessage.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
}
@Override
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java
index 043a57e6fd..93c16bf3e4 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -68,10 +68,10 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
static class AccessorLValue implements ValueRef {
- private PropertyOrFieldReference ref;
- private TypedValue contextObject;
- private EvaluationContext eContext;
- private boolean isAutoGrowNullReferences;
+ private final PropertyOrFieldReference ref;
+ private final TypedValue contextObject;
+ private final EvaluationContext eContext;
+ private final boolean isAutoGrowNullReferences;
public AccessorLValue(
PropertyOrFieldReference propertyOrFieldReference,
@@ -85,12 +85,12 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
@Override
public TypedValue getValue() {
- return ref.getValueInternal(contextObject,eContext,isAutoGrowNullReferences);
+ return this.ref.getValueInternal(this.contextObject,this.eContext,this.isAutoGrowNullReferences);
}
@Override
public void setValue(Object newValue) {
- ref.writeProperty(contextObject,eContext, ref.name, newValue);
+ this.ref.writeProperty(this.contextObject,this.eContext, this.ref.name, newValue);
}
@Override
@@ -142,7 +142,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
try {
if (isWritableProperty(this.name,contextObject,eContext)) {
Map,?> newMap = HashMap.class.newInstance();
- writeProperty(contextObject, eContext, name, newMap);
+ writeProperty(contextObject, eContext, this.name, newMap);
result = readProperty(contextObject, eContext, this.name);
}
}
@@ -161,7 +161,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
try {
if (isWritableProperty(this.name,contextObject,eContext)) {
Object newObject = result.getTypeDescriptor().getType().newInstance();
- writeProperty(contextObject, eContext, name, newObject);
+ writeProperty(contextObject, eContext, this.name, newObject);
result = readProperty(contextObject, eContext, this.name);
}
}
@@ -253,7 +253,7 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
private void writeProperty(TypedValue contextObject, EvaluationContext eContext, String name, Object newValue) throws SpelEvaluationException {
- if (contextObject.getValue() == null && nullSafe) {
+ if (contextObject.getValue() == null && this.nullSafe) {
return;
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java
index 427e33b767..99b4b13f53 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.ExpressionState;
/**
- * Represents a dot separated sequence of strings that indicate a package qualified type reference.
+ * Represents a dot separated sequence of strings that indicate a package qualified type
+ * reference.
*
* Example: "java.lang.String" as in the expression "new java.lang.String('hello')"
*
@@ -33,17 +34,19 @@ public class QualifiedIdentifier extends SpelNodeImpl {
// TODO safe to cache? dont think so
private TypedValue value;
+
public QualifiedIdentifier(int pos,SpelNodeImpl... operands) {
super(pos,operands);
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// Cache the concatenation of child identifiers
if (this.value == null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < getChildCount(); i++) {
- Object value = children[i].getValueInternal(state).getValue();
+ Object value = this.children[i].getValueInternal(state).getValue();
if (i > 0 && !value.toString().startsWith("$")) {
sb.append(".");
}
@@ -59,7 +62,8 @@ public class QualifiedIdentifier extends SpelNodeImpl {
StringBuilder sb = new StringBuilder();
if (this.value != null) {
sb.append(this.value.getValue());
- } else {
+ }
+ else {
for (int i = 0; i < getChildCount(); i++) {
if (i > 0) {
sb.append(".");
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java
index df2c110319..2a7f3d8744 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
/**
+ * Expression language AST node that represents a real literal.
+ *
* @author Andy Clement
* @since 3.0
*/
@@ -26,11 +28,13 @@ public class RealLiteral extends Literal {
private final TypedValue value;
+
public RealLiteral(String payload, int pos, double value) {
super(payload, pos);
this.value = new TypedValue(value);
}
+
@Override
public TypedValue getLiteralValue() {
return this.value;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java
index 710e975921..dfb838042f 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Selection.java
@@ -47,14 +47,18 @@ import org.springframework.util.ObjectUtils;
*/
public class Selection extends SpelNodeImpl {
- public final static int ALL = 0; // ?[]
- public final static int FIRST = 1; // ^[]
- public final static int LAST = 2; // $[]
+ public static final int ALL = 0; // ?[]
+
+ public static final int FIRST = 1; // ^[]
+
+ public static final int LAST = 2; // $[]
private final int variant;
+
private final boolean nullSafe;
- public Selection(boolean nullSafe, int variant,int pos,SpelNodeImpl expression) {
+
+ public Selection(boolean nullSafe, int variant, int pos, SpelNodeImpl expression) {
super(pos, expression != null ? new SpelNodeImpl[] { expression }
: new SpelNodeImpl[] {});
Assert.notNull(expression, "Expression must not be null");
@@ -72,7 +76,7 @@ public class Selection extends SpelNodeImpl {
TypedValue op = state.getActiveContextObject();
Object operand = op.getValue();
- SpelNodeImpl selectionCriteria = children[0];
+ SpelNodeImpl selectionCriteria = this.children[0];
if (operand instanceof Map) {
Map, ?> mapdata = (Map, ?>) operand;
// TODO don't lose generic info for the new map
@@ -85,32 +89,38 @@ public class Selection extends SpelNodeImpl {
Object o = selectionCriteria.getValueInternal(state).getValue();
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
- if (variant == FIRST) {
+ if (this.variant == FIRST) {
result.put(entry.getKey(),entry.getValue());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
}
result.put(entry.getKey(),entry.getValue());
lastKey = entry.getKey();
}
- } else {
+ }
+ else {
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
}
- } finally {
+ }
+ finally {
state.popActiveContextObject();
}
}
- if ((variant == FIRST || variant == LAST) && result.size() == 0) {
+ if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(null),this);
}
- if (variant == LAST) {
+
+ if (this.variant == LAST) {
Map resultMap = new HashMap();
Object lastValue = result.get(lastKey);
resultMap.put(lastKey,lastValue);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultMap),this);
}
+
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
- } else if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) {
+ }
+
+ if ((operand instanceof Collection) || ObjectUtils.isArray(operand)) {
List data = new ArrayList();
Collection> c = (operand instanceof Collection) ?
(Collection>) operand : Arrays.asList(ObjectUtils.toObjectArray(operand));
@@ -124,64 +134,64 @@ public class Selection extends SpelNodeImpl {
Object o = selectionCriteria.getValueInternal(state).getValue();
if (o instanceof Boolean) {
if (((Boolean) o).booleanValue() == true) {
- if (variant == FIRST) {
+ if (this.variant == FIRST) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(element),this);
}
result.add(element);
}
- } else {
+ }
+ else {
throw new SpelEvaluationException(selectionCriteria.getStartPosition(),
SpelMessage.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
}
idx++;
- } finally {
+ }
+ finally {
state.exitScope();
state.popActiveContextObject();
}
}
- if ((variant == FIRST || variant == LAST) && result.size() == 0) {
+
+ if ((this.variant == FIRST || this.variant == LAST) && result.size() == 0) {
return ValueRef.NullValueRef.instance;
}
- if (variant == LAST) {
+
+ if (this.variant == LAST) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result.get(result.size() - 1)),this);
}
+
if (operand instanceof Collection) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
}
- else {
- Class> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementTypeDescriptor().getType());
- Object resultArray = Array.newInstance(elementType, result.size());
- System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
- return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray),this);
- }
- } else {
- if (operand==null) {
- if (nullSafe) {
- return ValueRef.NullValueRef.instance;
- } else {
- throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
- "null");
- }
- } else {
- throw new SpelEvaluationException(getStartPosition(), SpelMessage.INVALID_TYPE_FOR_SELECTION,
- operand.getClass().getName());
- }
+ Class> elementType = ClassUtils.resolvePrimitiveIfNecessary(op.getTypeDescriptor().getElementTypeDescriptor().getType());
+ Object resultArray = Array.newInstance(elementType, result.size());
+ System.arraycopy(result.toArray(), 0, resultArray, 0, result.size());
+ return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultArray),this);
}
+ if (operand==null) {
+ if (this.nullSafe) {
+ return ValueRef.NullValueRef.instance;
+ }
+ throw new SpelEvaluationException(getStartPosition(),
+ SpelMessage.INVALID_TYPE_FOR_SELECTION, "null");
+ }
+ throw new SpelEvaluationException(getStartPosition(),
+ SpelMessage.INVALID_TYPE_FOR_SELECTION, operand.getClass().getName());
}
@Override
public String toStringAST() {
StringBuilder sb = new StringBuilder();
- switch (variant) {
- case ALL:
- sb.append("?[");
- break;
- case FIRST:
- sb.append("^[");
- break;
- case LAST:
- sb.append("$[");
- break;
+ switch (this.variant) {
+ case ALL:
+ sb.append("?[");
+ break;
+ case FIRST:
+ sb.append("^[");
+ break;
+ case LAST:
+ sb.append("$[");
+ break;
}
return sb.append(getChild(0).toStringAST()).append("]").toString();
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java
index 7964824a3c..f913035d0c 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/SpelNodeImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,8 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
/**
- * The common supertype of all AST nodes in a parsed Spring Expression Language format expression.
+ * The common supertype of all AST nodes in a parsed Spring Expression Language format
+ * expression.
*
* @author Andy Clement
* @since 3.0
@@ -37,9 +38,12 @@ public abstract class SpelNodeImpl implements SpelNode {
private static SpelNodeImpl[] NO_CHILDREN = new SpelNodeImpl[0];
protected int pos; // start = top 16bits, end = bottom 16bits
+
protected SpelNodeImpl[] children = SpelNodeImpl.NO_CHILDREN;
+
private SpelNodeImpl parent;
+
public SpelNodeImpl(int pos, SpelNodeImpl... operands) {
this.pos = pos;
// pos combines start and end so can never be zero because tokens cannot be zero length
@@ -52,11 +56,14 @@ public abstract class SpelNodeImpl implements SpelNode {
}
}
+
protected SpelNodeImpl getPreviousChild() {
SpelNodeImpl result = null;
- if (parent != null) {
- for (SpelNodeImpl child : parent.children) {
- if (this==child) break;
+ if (this.parent != null) {
+ for (SpelNodeImpl child : this.parent.children) {
+ if (this==child) {
+ break;
+ }
result = child;
}
}
@@ -67,21 +74,22 @@ public abstract class SpelNodeImpl implements SpelNode {
* @return true if the next child is one of the specified classes
*/
protected boolean nextChildIs(Class... clazzes) {
- if (parent!=null) {
- SpelNodeImpl[] peers = parent.children;
- for (int i=0,max=peers.length;i=max) {
- return false;
- } else {
- Class clazz = peers[i+1].getClass();
- for (Class desiredClazz: clazzes) {
- if (clazz.equals(desiredClazz)) {
- return true;
- }
- }
+ if (this.parent != null) {
+ SpelNodeImpl[] peers = this.parent.children;
+ for (int i = 0, max = peers.length; i < max; i++) {
+ if (peers[i] == this) {
+ if ((i + 1) >= max) {
return false;
}
+
+ Class clazz = peers[i + 1].getClass();
+ for (Class desiredClazz : clazzes) {
+ if (clazz.equals(desiredClazz)) {
+ return true;
+ }
+ }
+
+ return false;
}
}
}
@@ -92,7 +100,8 @@ public abstract class SpelNodeImpl implements SpelNode {
public final Object getValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState != null) {
return getValueInternal(expressionState).getValue();
- } else {
+ }
+ else {
// configuration not set - does that matter?
return getValue(new ExpressionState(new StandardEvaluationContext()));
}
@@ -102,7 +111,8 @@ public abstract class SpelNodeImpl implements SpelNode {
public final TypedValue getTypedValue(ExpressionState expressionState) throws EvaluationException {
if (expressionState != null) {
return getValueInternal(expressionState);
- } else {
+ }
+ else {
// configuration not set - does that matter?
return getTypedValue(new ExpressionState(new StandardEvaluationContext()));
}
@@ -116,17 +126,18 @@ public abstract class SpelNodeImpl implements SpelNode {
@Override
public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
- throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
+ throw new SpelEvaluationException(getStartPosition(),
+ SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
}
@Override
public SpelNode getChild(int index) {
- return children[index];
+ return this.children[index];
}
@Override
public int getChildCount() {
- return children.length;
+ return this.children.length;
}
@Override
@@ -148,15 +159,15 @@ public abstract class SpelNodeImpl implements SpelNode {
@Override
public int getStartPosition() {
- return (pos>>16);
+ return (this.pos>>16);
}
@Override
public int getEndPosition() {
- return (pos&0xffff);
+ return (this.pos&0xffff);
}
protected ValueRef getValueRef(ExpressionState state) throws EvaluationException {
- throw new SpelEvaluationException(pos,SpelMessage.NOT_ASSIGNABLE,toStringAST());
+ throw new SpelEvaluationException(this.pos,SpelMessage.NOT_ASSIGNABLE,toStringAST());
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java
index efcd829726..3755a86284 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.springframework.expression.spel.ast;
import org.springframework.expression.TypedValue;
/**
+ * Expression language AST node that represents a string literal.
+ *
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
@@ -27,6 +29,7 @@ public class StringLiteral extends Literal {
private final TypedValue value;
+
public StringLiteral(String payload, int pos, String value) {
super(payload,pos);
// TODO should these have been skipped being created by the parser rules? or not?
@@ -34,6 +37,7 @@ public class StringLiteral extends Literal {
this.value = new TypedValue(value.replaceAll("''", "'").replaceAll("\"\"", "\""));
}
+
@Override
public TypedValue getLiteralValue() {
return this.value;
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java
index 3ee71fc88f..c637adfb86 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Ternary.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,22 +37,24 @@ public class Ternary extends SpelNodeImpl {
}
/**
- * Evaluate the condition and if true evaluate the first alternative, otherwise evaluate the second alternative.
+ * Evaluate the condition and if true evaluate the first alternative, otherwise
+ * evaluate the second alternative.
* @param state the expression state
- * @throws EvaluationException if the condition does not evaluate correctly to a boolean or there is a problem
- * executing the chosen alternative
+ * @throws EvaluationException if the condition does not evaluate correctly to a
+ * boolean or there is a problem executing the chosen alternative
*/
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
- Boolean value = children[0].getValue(state, Boolean.class);
+ Boolean value = this.children[0].getValue(state, Boolean.class);
if (value == null) {
throw new SpelEvaluationException(getChild(0).getStartPosition(),
SpelMessage.TYPE_CONVERSION_ERROR, "null", "boolean");
}
if (value.booleanValue()) {
- return children[1].getValueInternal(state);
- } else {
- return children[2].getValueInternal(state);
+ return this.children[1].getValueInternal(state);
+ }
+ else {
+ return this.children[2].getValueInternal(state);
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java
index e8a2ac231a..6a477af880 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeCode.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,26 +16,45 @@
package org.springframework.expression.spel.ast;
/**
- * Captures primitive types and their corresponding class objects, plus one special entry that represents all reference
- * (non-primitive) types.
+ * Captures primitive types and their corresponding class objects, plus one special entry
+ * that represents all reference (non-primitive) types.
*
* @author Andy Clement
*/
public enum TypeCode {
- OBJECT(Object.class), BOOLEAN(Boolean.TYPE), BYTE(Byte.TYPE), CHAR(Character.TYPE), //
- SHORT(Short.TYPE), INT(Integer.TYPE), LONG(Long.TYPE), FLOAT(Float.TYPE), DOUBLE(Double.TYPE);
+ OBJECT(Object.class),
+
+ BOOLEAN(Boolean.TYPE),
+
+ BYTE(Byte.TYPE),
+
+ CHAR(Character.TYPE),
+
+ SHORT(Short.TYPE),
+
+ INT(Integer.TYPE),
+
+ LONG(Long.TYPE),
+
+ FLOAT(Float.TYPE),
+
+ DOUBLE(Double.TYPE);
+
private Class> type;
+
TypeCode(Class> type) {
this.type = type;
}
+
public Class> getType() {
- return type;
+ return this.type;
}
+
public static TypeCode forName(String name) {
String searchingFor = name.toUpperCase();
TypeCode[] tcs = values();
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java
index 9f9bb0af59..9471486338 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/TypeReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,21 +29,23 @@ import org.springframework.expression.spel.ExpressionState;
*/
public class TypeReference extends SpelNodeImpl {
- private int dimensions;
+ private final int dimensions;
- public TypeReference(int pos,SpelNodeImpl qualifiedId) {
+
+ public TypeReference(int pos, SpelNodeImpl qualifiedId) {
this(pos,qualifiedId,0);
}
- public TypeReference(int pos,SpelNodeImpl qualifiedId,int dims) {
+ public TypeReference(int pos, SpelNodeImpl qualifiedId, int dims) {
super(pos,qualifiedId);
this.dimensions = dims;
}
+
@Override
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
// TODO possible optimization here if we cache the discovered type reference, but can we do that?
- String typename = (String) children[0].getValueInternal(state).getValue();
+ String typename = (String) this.children[0].getValueInternal(state).getValue();
if (typename.indexOf(".") == -1 && Character.isLowerCase(typename.charAt(0))) {
TypeCode tc = TypeCode.valueOf(typename.toUpperCase());
if (tc != TypeCode.OBJECT) {
@@ -59,8 +61,8 @@ public class TypeReference extends SpelNodeImpl {
}
private Class makeArrayIfNecessary(Class clazz) {
- if (dimensions!=0) {
- for (int i=0;i constructedNodes = new Stack();
+ private final Stack constructedNodes = new Stack();
- private SpelParserConfiguration configuration;
+ private final SpelParserConfiguration configuration;
/**
@@ -77,16 +78,16 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
this.expressionString = expressionString;
Tokenizer tokenizer = new Tokenizer(expressionString);
tokenizer.process();
- tokenStream = tokenizer.getTokens();
- tokenStreamLength = tokenStream.size();
- tokenStreamPointer = 0;
- constructedNodes.clear();
+ this.tokenStream = tokenizer.getTokens();
+ this.tokenStreamLength = this.tokenStream.size();
+ this.tokenStreamPointer = 0;
+ this.constructedNodes.clear();
SpelNodeImpl ast = eatExpression();
if (moreTokens()) {
throw new SpelParseException(peekToken().startpos,SpelMessage.MORE_INPUT,toString(nextToken()));
}
- Assert.isTrue(constructedNodes.isEmpty());
- return new SpelExpression(expressionString, ast, configuration);
+ Assert.isTrue(this.constructedNodes.isEmpty());
+ return new SpelExpression(expressionString, ast, this.configuration);
}
catch (InternalParseException ipe) {
throw ipe.getCause();
@@ -110,7 +111,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
nextToken();
SpelNodeImpl assignedValue = eatLogicalOrExpression();
return new Assign(toPos(t),expr,assignedValue);
- } else if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
+ }
+
+ if (t.kind==TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b)
if (expr==null) {
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-2));
}
@@ -120,7 +123,9 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
valueIfNull = new NullLiteral(toPos(t.startpos+1,t.endpos+1));
}
return new Elvis(toPos(t),expr,valueIfNull);
- } else if (t.kind==TokenKind.QMARK) { // a?b:c
+ }
+
+ if (t.kind==TokenKind.QMARK) { // a?b:c
if (expr==null) {
expr = new NullLiteral(toPos(t.startpos-1,t.endpos-1));
}
@@ -167,31 +172,38 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
SpelNodeImpl rhExpr = eatSumExpression();
checkOperands(t,expr,rhExpr);
TokenKind tk = relationalOperatorToken.kind;
+
if (relationalOperatorToken.isNumericRelationalOperator()) {
int pos = toPos(t);
- if (tk==TokenKind.GT) {
- return new OpGT(pos,expr,rhExpr);
- } else if (tk==TokenKind.LT) {
- return new OpLT(pos,expr,rhExpr);
- } else if (tk==TokenKind.LE) {
- return new OpLE(pos,expr,rhExpr);
- } else if (tk==TokenKind.GE) {
- return new OpGE(pos,expr,rhExpr);
- } else if (tk == TokenKind.EQ) {
- return new OpEQ(pos,expr,rhExpr);
- } else {
- Assert.isTrue(tk == TokenKind.NE);
- return new OpNE(pos,expr,rhExpr);
+ if (tk == TokenKind.GT) {
+ return new OpGT(pos, expr, rhExpr);
}
+ if (tk == TokenKind.LT) {
+ return new OpLT(pos, expr, rhExpr);
+ }
+ if (tk == TokenKind.LE) {
+ return new OpLE(pos, expr, rhExpr);
+ }
+ if (tk == TokenKind.GE) {
+ return new OpGE(pos, expr, rhExpr);
+ }
+ if (tk == TokenKind.EQ) {
+ return new OpEQ(pos, expr, rhExpr);
+ }
+ Assert.isTrue(tk == TokenKind.NE);
+ return new OpNE(pos, expr, rhExpr);
}
- if (tk==TokenKind.INSTANCEOF) {
- return new OperatorInstanceof(toPos(t),expr,rhExpr);
- } else if (tk==TokenKind.MATCHES) {
- return new OperatorMatches(toPos(t),expr,rhExpr);
- } else {
- Assert.isTrue(tk==TokenKind.BETWEEN);
- return new org.springframework.expression.spel.ast.OperatorBetween(toPos(t),expr,rhExpr);
+
+ if (tk == TokenKind.INSTANCEOF) {
+ return new OperatorInstanceof(toPos(t), expr, rhExpr);
}
+
+ if (tk == TokenKind.MATCHES) {
+ return new OperatorMatches(toPos(t), expr, rhExpr);
+ }
+
+ Assert.isTrue(tk == TokenKind.BETWEEN);
+ return new OperatorBetween(toPos(t), expr, rhExpr);
}
return expr;
}
@@ -199,14 +211,15 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
//sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*;
private SpelNodeImpl eatSumExpression() {
SpelNodeImpl expr = eatProductExpression();
- while (peekToken(TokenKind.PLUS,TokenKind.MINUS,TokenKind.INC)) {
+ while (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) {
Token t = nextToken();//consume PLUS or MINUS or INC
SpelNodeImpl rhExpr = eatProductExpression();
checkRightOperand(t,rhExpr);
- if (t.kind==TokenKind.PLUS) {
- expr = new OpPlus(toPos(t),expr,rhExpr);
- } else if (t.kind==TokenKind.MINUS) {
- expr = new OpMinus(toPos(t),expr,rhExpr);
+ if (t.kind == TokenKind.PLUS) {
+ expr = new OpPlus(toPos(t), expr, rhExpr);
+ }
+ else if (t.kind == TokenKind.MINUS) {
+ expr = new OpMinus(toPos(t), expr, rhExpr);
}
}
return expr;
@@ -215,17 +228,19 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ;
private SpelNodeImpl eatProductExpression() {
SpelNodeImpl expr = eatPowerIncDecExpression();
- while (peekToken(TokenKind.STAR,TokenKind.DIV,TokenKind.MOD)) {
+ while (peekToken(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) {
Token t = nextToken(); // consume STAR/DIV/MOD
SpelNodeImpl rhExpr = eatPowerIncDecExpression();
checkOperands(t,expr,rhExpr);
- if (t.kind==TokenKind.STAR) {
- expr = new OpMultiply(toPos(t),expr,rhExpr);
- } else if (t.kind==TokenKind.DIV) {
- expr = new OpDivide(toPos(t),expr,rhExpr);
- } else {
- Assert.isTrue(t.kind==TokenKind.MOD);
- expr = new OpModulus(toPos(t),expr,rhExpr);
+ if (t.kind == TokenKind.STAR) {
+ expr = new OpMultiply(toPos(t), expr, rhExpr);
+ }
+ else if (t.kind == TokenKind.DIV) {
+ expr = new OpDivide(toPos(t), expr, rhExpr);
+ }
+ else {
+ Assert.isTrue(t.kind == TokenKind.MOD);
+ expr = new OpModulus(toPos(t), expr, rhExpr);
}
}
return expr;
@@ -239,41 +254,45 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
SpelNodeImpl rhExpr = eatUnaryExpression();
checkRightOperand(t,rhExpr);
return new OperatorPower(toPos(t),expr, rhExpr);
- } else if (expr!=null && peekToken(TokenKind.INC,TokenKind.DEC)) {
+ }
+
+ if (expr!=null && peekToken(TokenKind.INC,TokenKind.DEC)) {
Token t = nextToken();//consume INC/DEC
if (t.getKind()==TokenKind.INC) {
return new OpInc(toPos(t),true,expr);
- } else {
- return new OpDec(toPos(t),true,expr);
}
+ return new OpDec(toPos(t),true,expr);
}
+
return expr;
}
// unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ;
private SpelNodeImpl eatUnaryExpression() {
- if (peekToken(TokenKind.PLUS,TokenKind.MINUS,TokenKind.NOT)) {
+ if (peekToken(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) {
Token t = nextToken();
SpelNodeImpl expr = eatUnaryExpression();
- if (t.kind==TokenKind.NOT) {
- return new OperatorNot(toPos(t),expr);
- } else if (t.kind==TokenKind.PLUS) {
- return new OpPlus(toPos(t),expr);
- } else {
- Assert.isTrue(t.kind==TokenKind.MINUS);
- return new OpMinus(toPos(t),expr);
+ if (t.kind == TokenKind.NOT) {
+ return new OperatorNot(toPos(t), expr);
}
- } else if (peekToken(TokenKind.INC,TokenKind.DEC)) {
- Token t = nextToken();
- SpelNodeImpl expr = eatUnaryExpression();
- if (t.getKind()==TokenKind.INC) {
- return new OpInc(toPos(t),false,expr);
- } else {
- return new OpDec(toPos(t),false,expr);
+
+ if (t.kind == TokenKind.PLUS) {
+ return new OpPlus(toPos(t), expr);
}
- } else {
- return eatPrimaryExpression();
+ Assert.isTrue(t.kind == TokenKind.MINUS);
+ return new OpMinus(toPos(t), expr);
+
}
+ if (peekToken(TokenKind.INC, TokenKind.DEC)) {
+ Token t = nextToken();
+ SpelNodeImpl expr = eatUnaryExpression();
+ if (t.getKind() == TokenKind.INC) {
+ return new OpInc(toPos(t), false, expr);
+ }
+ return new OpDec(toPos(t), false, expr);
+ }
+
+ return eatPrimaryExpression();
}
// primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?);
@@ -284,11 +303,12 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
while (maybeEatNode()) {
nodes.add(pop());
}
- if (nodes.size()==1) {
+ if (nodes.size() == 1) {
return nodes.get(0);
- } else {
- return new CompoundExpression(toPos(start.getStartPosition(),nodes.get(nodes.size()-1).getEndPosition()),nodes.toArray(new SpelNodeImpl[nodes.size()]));
}
+ return new CompoundExpression(toPos(start.getStartPosition(),
+ nodes.get(nodes.size() - 1).getEndPosition()),
+ nodes.toArray(new SpelNodeImpl[nodes.size()]));
}
// node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+;
@@ -299,6 +319,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
} else {
expr = maybeEatNonDottedNode();
}
+
if (expr==null) {
return false;
} else {
@@ -328,15 +349,19 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// ;
private SpelNodeImpl eatDottedNode() {
Token t = nextToken();// it was a '.' or a '?.'
- boolean nullSafeNavigation = t.kind==TokenKind.SAFE_NAVI;
- if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() || maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) {
+ boolean nullSafeNavigation = t.kind == TokenKind.SAFE_NAVI;
+ if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar()
+ || maybeEatProjection(nullSafeNavigation)
+ || maybeEatSelection(nullSafeNavigation)) {
return pop();
}
- if (peekToken()==null) {
+ if (peekToken() == null) {
// unexpectedly ran out of data
- raiseInternalException(t.startpos,SpelMessage.OOD);
- } else {
- raiseInternalException(t.startpos,SpelMessage.UNEXPECTED_DATA_AFTER_DOT,toString(peekToken()));
+ raiseInternalException(t.startpos, SpelMessage.OOD);
+ }
+ else {
+ raiseInternalException(t.startpos, SpelMessage.UNEXPECTED_DATA_AFTER_DOT,
+ toString(peekToken()));
}
return null;
}
@@ -354,13 +379,15 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
Token t = nextToken();
Token functionOrVariableName = eatToken(TokenKind.IDENTIFIER);
SpelNodeImpl[] args = maybeEatMethodArgs();
- if (args==null) {
- push(new VariableReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos)));
- return true;
- } else {
- push(new FunctionReference(functionOrVariableName.data,toPos(t.startpos,functionOrVariableName.endpos),args));
+ if (args == null) {
+ push(new VariableReference(functionOrVariableName.data, toPos(t.startpos,
+ functionOrVariableName.endpos)));
return true;
}
+
+ push(new FunctionReference(functionOrVariableName.data, toPos(t.startpos,
+ functionOrVariableName.endpos), args));
+ return true;
}
// methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!;
@@ -376,7 +403,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private void eatConstructorArgs(List accumulatedArguments) {
if (!peekToken(TokenKind.LPAREN)) {
- throw new InternalParseException(new SpelParseException(expressionString,positionOf(peekToken()),SpelMessage.MISSING_CONSTRUCTOR_ARGS));
+ throw new InternalParseException(new SpelParseException(this.expressionString,positionOf(peekToken()),SpelMessage.MISSING_CONSTRUCTOR_ARGS));
}
consumeArguments(accumulatedArguments);
eatToken(TokenKind.RPAREN);
@@ -391,27 +418,28 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
do {
nextToken();// consume ( (first time through) or comma (subsequent times)
Token t = peekToken();
- if (t==null) {
- raiseInternalException(pos,SpelMessage.RUN_OUT_OF_ARGUMENTS);
+ if (t == null) {
+ raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS);
}
- if (t.kind!=TokenKind.RPAREN) {
+ if (t.kind != TokenKind.RPAREN) {
accumulatedArguments.add(eatExpression());
}
next = peekToken();
- } while (next!=null && next.kind==TokenKind.COMMA);
- if (next==null) {
- raiseInternalException(pos,SpelMessage.RUN_OUT_OF_ARGUMENTS);
+ }
+ while (next != null && next.kind == TokenKind.COMMA);
+
+ if (next == null) {
+ raiseInternalException(pos, SpelMessage.RUN_OUT_OF_ARGUMENTS);
}
}
private int positionOf(Token t) {
- if (t==null) {
+ if (t == null) {
// if null assume the problem is because the right token was
// not found at the end of the expression
- return expressionString.length();
- } else {
- return t.startpos;
+ return this.expressionString.length();
}
+ return t.startpos;
}
//startNode
@@ -428,17 +456,26 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private SpelNodeImpl eatStartNode() {
if (maybeEatLiteral()) {
return pop();
- } else if (maybeEatParenExpression()) {
+ }
+ else if (maybeEatParenExpression()) {
return pop();
- } else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) {
+ }
+ else if (maybeEatTypeReference() || maybeEatNullReference()
+ || maybeEatConstructorReference() || maybeEatMethodOrProperty(false)
+ || maybeEatFunctionOrVar()) {
return pop();
- } else if (maybeEatBeanReference()) {
+ }
+ else if (maybeEatBeanReference()) {
return pop();
- } else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) {
+ }
+ else if (maybeEatProjection(false) || maybeEatSelection(false)
+ || maybeEatIndexer()) {
return pop();
- } else if (maybeEatInlineList()) {
+ }
+ else if (maybeEatInlineList()) {
return pop();
- } else {
+ }
+ else {
return null;
}
}
@@ -453,16 +490,19 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
if (peekToken(TokenKind.IDENTIFIER)) {
beanNameToken = eatToken(TokenKind.IDENTIFIER);
beanname = beanNameToken.data;
- } else if (peekToken(TokenKind.LITERAL_STRING)) {
+ }
+ else if (peekToken(TokenKind.LITERAL_STRING)) {
beanNameToken = eatToken(TokenKind.LITERAL_STRING);
beanname = beanNameToken.stringValue();
beanname = beanname.substring(1, beanname.length() - 1);
- } else {
- raiseInternalException(beanRefToken.startpos,SpelMessage.INVALID_BEAN_REFERENCE);
+ }
+ else {
+ raiseInternalException(beanRefToken.startpos,
+ SpelMessage.INVALID_BEAN_REFERENCE);
}
BeanReference beanReference = new BeanReference(toPos(beanNameToken),beanname);
- constructedNodes.push(beanReference);
+ this.constructedNodes.push(beanReference);
return true;
}
return false;
@@ -485,7 +525,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
dims++;
}
eatToken(TokenKind.RPAREN);
- constructedNodes.push(new TypeReference(toPos(typeName),node,dims));
+ this.constructedNodes.push(new TypeReference(toPos(typeName),node,dims));
return true;
}
return false;
@@ -498,7 +538,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
return false;
}
nextToken();
- constructedNodes.push(new NullLiteral(toPos(nullToken)));
+ this.constructedNodes.push(new NullLiteral(toPos(nullToken)));
return true;
}
return false;
@@ -507,46 +547,48 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
//projection: PROJECT^ expression RCURLY!;
private boolean maybeEatProjection(boolean nullSafeNavigation) {
Token t = peekToken();
- if (!peekToken(TokenKind.PROJECT,true)) {
+ if (!peekToken(TokenKind.PROJECT, true)) {
return false;
}
SpelNodeImpl expr = eatExpression();
eatToken(TokenKind.RSQUARE);
- constructedNodes.push(new Projection(nullSafeNavigation, toPos(t), expr));
+ this.constructedNodes.push(new Projection(nullSafeNavigation, toPos(t), expr));
return true;
}
// list = LCURLY (element (COMMA element)*) RCURLY
private boolean maybeEatInlineList() {
Token t = peekToken();
- if (!peekToken(TokenKind.LCURLY,true)) {
+ if (!peekToken(TokenKind.LCURLY, true)) {
return false;
}
SpelNodeImpl expr = null;
Token closingCurly = peekToken();
- if (peekToken(TokenKind.RCURLY,true)) {
+ if (peekToken(TokenKind.RCURLY, true)) {
// empty list '[]'
expr = new InlineList(toPos(t.startpos,closingCurly.endpos));
- } else {
+ }
+ else {
List listElements = new ArrayList();
do {
listElements.add(eatExpression());
} while (peekToken(TokenKind.COMMA,true));
+
closingCurly = eatToken(TokenKind.RCURLY);
expr = new InlineList(toPos(t.startpos,closingCurly.endpos),listElements.toArray(new SpelNodeImpl[listElements.size()]));
}
- constructedNodes.push(expr);
+ this.constructedNodes.push(expr);
return true;
}
private boolean maybeEatIndexer() {
Token t = peekToken();
- if (!peekToken(TokenKind.LSQUARE,true)) {
+ if (!peekToken(TokenKind.LSQUARE, true)) {
return false;
}
SpelNodeImpl expr = eatExpression();
eatToken(TokenKind.RSQUARE);
- constructedNodes.push(new Indexer(toPos(t),expr));
+ this.constructedNodes.push(new Indexer(toPos(t),expr));
return true;
}
@@ -561,12 +603,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
raiseInternalException(toPos(t), SpelMessage.MISSING_SELECTION_EXPRESSION);
}
eatToken(TokenKind.RSQUARE);
- if (t.kind==TokenKind.SELECT_FIRST) {
- constructedNodes.push(new Selection(nullSafeNavigation,Selection.FIRST,toPos(t),expr));
- } else if (t.kind==TokenKind.SELECT_LAST) {
- constructedNodes.push(new Selection(nullSafeNavigation,Selection.LAST,toPos(t),expr));
- } else {
- constructedNodes.push(new Selection(nullSafeNavigation,Selection.ALL,toPos(t),expr));
+ if (t.kind == TokenKind.SELECT_FIRST) {
+ this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.FIRST, toPos(t), expr));
+ }
+ else if (t.kind == TokenKind.SELECT_LAST) {
+ this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.LAST, toPos(t), expr));
+ }
+ else {
+ this.constructedNodes.push(new Selection(nullSafeNavigation, Selection.ALL, toPos(t), expr));
}
return true;
}
@@ -587,7 +631,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
if(qualifiedIdPieces.isEmpty()) {
if(node == null) {
- raiseInternalException( expressionString.length(), SpelMessage.OOD);
+ raiseInternalException( this.expressionString.length(), SpelMessage.OOD);
}
raiseInternalException(node.startpos, SpelMessage.NOT_EXPECTED_TOKEN,
"qualified ID", node.getKind().toString().toLowerCase());
@@ -617,13 +661,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
// property
push(new PropertyOrFieldReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName)));
return true;
- } else {
- // methodreference
- push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args));
- // TODO what is the end position for a method reference? the name or the last arg?
- return true;
}
+ // methodreference
+ push(new MethodReference(nullSafeNavigation, methodOrPropertyName.data,toPos(methodOrPropertyName),args));
+ // TODO what is the end position for a method reference? the name or the last arg?
+ return true;
}
+
return false;
}
@@ -642,7 +686,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
while (peekToken(TokenKind.LSQUARE,true)) {
if (!peekToken(TokenKind.RSQUARE)) {
dimensions.add(eatExpression());
- } else {
+ }
+ else {
dimensions.add(null);
}
eatToken(TokenKind.RSQUARE);
@@ -652,11 +697,13 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
push(new ConstructorReference(toPos(newToken), dimensions.toArray(new SpelNodeImpl[dimensions.size()]),
nodes.toArray(new SpelNodeImpl[nodes.size()])));
- } else {
+ }
+ else {
// regular constructor invocation
eatConstructorArgs(nodes);
// TODO correct end position?
- push(new ConstructorReference(toPos(newToken), nodes.toArray(new SpelNodeImpl[nodes.size()])));
+ push(new ConstructorReference(toPos(newToken),
+ nodes.toArray(new SpelNodeImpl[nodes.size()])));
}
return true;
}
@@ -664,11 +711,11 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
private void push(SpelNodeImpl newNode) {
- constructedNodes.push(newNode);
+ this.constructedNodes.push(newNode);
}
private SpelNodeImpl pop() {
- return constructedNodes.pop();
+ return this.constructedNodes.pop();
}
// literal
@@ -684,25 +731,34 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
if (t==null) {
return false;
}
- if (t.kind==TokenKind.LITERAL_INT) {
+ if (t.kind == TokenKind.LITERAL_INT) {
push(Literal.getIntLiteral(t.data, toPos(t), 10));
- } else if (t.kind==TokenKind.LITERAL_LONG) {
+ }
+ else if (t.kind == TokenKind.LITERAL_LONG) {
push(Literal.getLongLiteral(t.data, toPos(t), 10));
- } else if (t.kind==TokenKind.LITERAL_HEXINT) {
+ }
+ else if (t.kind == TokenKind.LITERAL_HEXINT) {
push(Literal.getIntLiteral(t.data, toPos(t), 16));
- } else if (t.kind==TokenKind.LITERAL_HEXLONG) {
+ }
+ else if (t.kind == TokenKind.LITERAL_HEXLONG) {
push(Literal.getLongLiteral(t.data, toPos(t), 16));
- } else if (t.kind==TokenKind.LITERAL_REAL) {
- push(Literal.getRealLiteral(t.data, toPos(t),false));
- } else if (t.kind==TokenKind.LITERAL_REAL_FLOAT) {
+ }
+ else if (t.kind == TokenKind.LITERAL_REAL) {
+ push(Literal.getRealLiteral(t.data, toPos(t), false));
+ }
+ else if (t.kind == TokenKind.LITERAL_REAL_FLOAT) {
push(Literal.getRealLiteral(t.data, toPos(t), true));
- } else if (peekIdentifierToken("true")) {
- push(new BooleanLiteral(t.data,toPos(t),true));
- } else if (peekIdentifierToken("false")) {
- push(new BooleanLiteral(t.data,toPos(t),false));
- } else if (t.kind==TokenKind.LITERAL_STRING) {
- push(new StringLiteral(t.data,toPos(t),t.data));
- } else {
+ }
+ else if (peekIdentifierToken("true")) {
+ push(new BooleanLiteral(t.data, toPos(t), true));
+ }
+ else if (peekIdentifierToken("false")) {
+ push(new BooleanLiteral(t.data, toPos(t), false));
+ }
+ else if (t.kind == TokenKind.LITERAL_STRING) {
+ push(new StringLiteral(t.data, toPos(t), t.data));
+ }
+ else {
return false;
}
nextToken();
@@ -717,7 +773,8 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
eatToken(TokenKind.RPAREN);
push(expr);
return true;
- } else {
+ }
+ else {
return false;
}
}
@@ -737,9 +794,11 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
String idString = t.stringValue();
if (idString.equalsIgnoreCase("instanceof")) {
return t.asInstanceOfToken();
- } else if (idString.equalsIgnoreCase("matches")) {
+ }
+ if (idString.equalsIgnoreCase("matches")) {
return t.asMatchesToken();
- } else if (idString.equalsIgnoreCase("between")) {
+ }
+ if (idString.equalsIgnoreCase("between")) {
return t.asBetweenToken();
}
}
@@ -749,7 +808,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
private Token eatToken(TokenKind expectedKind) {
Token t = nextToken();
if (t==null) {
- raiseInternalException( expressionString.length(), SpelMessage.OOD);
+ raiseInternalException( this.expressionString.length(), SpelMessage.OOD);
}
if (t.kind!=expectedKind) {
raiseInternalException(t.startpos,SpelMessage.NOT_EXPECTED_TOKEN, expectedKind.toString().toLowerCase(),t.getKind().toString().toLowerCase());
@@ -768,32 +827,36 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
Token t = peekToken();
if (t.kind==desiredTokenKind) {
if (consumeIfMatched) {
- tokenStreamPointer++;
+ this.tokenStreamPointer++;
}
return true;
- } else {
- if (desiredTokenKind == TokenKind.IDENTIFIER) {
- // might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier
- // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum
- if (t.kind.ordinal()>=TokenKind.DIV.ordinal() && t.kind.ordinal()<=TokenKind.NOT.ordinal() && t.data!=null) {
- // if t.data were null, we'd know it wasn't the textual form, it was the symbol form
- return true;
- }
- }
- return false;
}
+
+ if (desiredTokenKind == TokenKind.IDENTIFIER) {
+ // might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier
+ // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum
+ if (t.kind.ordinal()>=TokenKind.DIV.ordinal() && t.kind.ordinal()<=TokenKind.NOT.ordinal() && t.data!=null) {
+ // if t.data were null, we'd know it wasn't the textual form, it was the symbol form
+ return true;
+ }
+ }
+ return false;
}
private boolean peekToken(TokenKind possible1,TokenKind possible2) {
- if (!moreTokens()) return false;
+ if (!moreTokens()) {
+ return false;
+ }
Token t = peekToken();
- return t.kind==possible1 || t.kind==possible2;
+ return t.kind == possible1 || t.kind == possible2;
}
private boolean peekToken(TokenKind possible1,TokenKind possible2, TokenKind possible3) {
- if (!moreTokens()) return false;
+ if (!moreTokens()) {
+ return false;
+ }
Token t = peekToken();
- return t.kind==possible1 || t.kind==possible2 || t.kind==possible3;
+ return t.kind == possible1 || t.kind == possible2 || t.kind == possible3;
}
private boolean peekIdentifierToken(String identifierString) {
@@ -805,39 +868,42 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
}
private boolean peekSelectToken() {
- if (!moreTokens()) return false;
+ if (!moreTokens()) {
+ return false;
+ }
Token t = peekToken();
- return t.kind==TokenKind.SELECT || t.kind==TokenKind.SELECT_FIRST || t.kind==TokenKind.SELECT_LAST;
+ return t.kind == TokenKind.SELECT || t.kind == TokenKind.SELECT_FIRST
+ || t.kind == TokenKind.SELECT_LAST;
}
private boolean moreTokens() {
- return tokenStreamPointer=tokenStreamLength) {
+ if (this.tokenStreamPointer >= this.tokenStreamLength) {
return null;
}
- return tokenStream.get(tokenStreamPointer++);
+ return this.tokenStream.get(this.tokenStreamPointer++);
}
private Token peekToken() {
- if (tokenStreamPointer>=tokenStreamLength) {
+ if (this.tokenStreamPointer >= this.tokenStreamLength) {
return null;
}
- return tokenStream.get(tokenStreamPointer);
+ return this.tokenStream.get(this.tokenStreamPointer);
}
- private void raiseInternalException(int pos, SpelMessage message,Object... inserts) {
- throw new InternalParseException(new SpelParseException(expressionString,pos,message,inserts));
+ private void raiseInternalException(int pos, SpelMessage message, Object... inserts) {
+ throw new InternalParseException(new SpelParseException(this.expressionString,
+ pos, message, inserts));
}
public String toString(Token t) {
if (t.getKind().hasPayload()) {
return t.stringValue();
- } else {
- return t.kind.toString().toLowerCase();
}
+ return t.kind.toString().toLowerCase();
}
private void checkOperands(Token token, SpelNodeImpl left, SpelNodeImpl right) {
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java
index dfefb5516d..18afa0be62 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/SpelExpression.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,10 +30,10 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
/**
- * A {@code SpelExpression} represents a parsed (valid) expression that is ready
- * to be evaluated in a specified context. An expression can be evaluated
- * standalone or in a specified context. During expression evaluation the context
- * may be asked to resolve references to types, beans, properties, and methods.
+ * A {@code SpelExpression} represents a parsed (valid) expression that is ready to be
+ * evaluated in a specified context. An expression can be evaluated standalone or in a
+ * specified context. During expression evaluation the context may be asked to resolve
+ * references to types, beans, properties, and methods.
*
* @author Andy Clement
* @since 3.0
@@ -64,51 +64,51 @@ public class SpelExpression implements Expression {
@Override
public Object getValue() throws EvaluationException {
- ExpressionState expressionState = new ExpressionState(getEvaluationContext(), configuration);
- return ast.getValue(expressionState);
+ ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
+ return this.ast.getValue(expressionState);
}
@Override
public Object getValue(Object rootObject) throws EvaluationException {
- ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
- return ast.getValue(expressionState);
+ ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration);
+ return this.ast.getValue(expressionState);
}
@Override
public T getValue(Class expectedResultType) throws EvaluationException {
- ExpressionState expressionState = new ExpressionState(getEvaluationContext(), configuration);
- TypedValue typedResultValue = ast.getTypedValue(expressionState);
+ ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
+ TypedValue typedResultValue = this.ast.getTypedValue(expressionState);
return ExpressionUtils.convertTypedValue(expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
}
@Override
public T getValue(Object rootObject, Class expectedResultType) throws EvaluationException {
- ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
- TypedValue typedResultValue = ast.getTypedValue(expressionState);
+ ExpressionState expressionState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration);
+ TypedValue typedResultValue = this.ast.getTypedValue(expressionState);
return ExpressionUtils.convertTypedValue(expressionState.getEvaluationContext(), typedResultValue, expectedResultType);
}
@Override
public Object getValue(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- return ast.getValue(new ExpressionState(context, configuration));
+ return this.ast.getValue(new ExpressionState(context, this.configuration));
}
@Override
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- return ast.getValue(new ExpressionState(context, toTypedValue(rootObject), configuration));
+ return this.ast.getValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration));
}
@Override
public T getValue(EvaluationContext context, Class expectedResultType) throws EvaluationException {
- TypedValue typedResultValue = ast.getTypedValue(new ExpressionState(context, configuration));
+ TypedValue typedResultValue = this.ast.getTypedValue(new ExpressionState(context, this.configuration));
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
}
@Override
public T getValue(EvaluationContext context, Object rootObject, Class expectedResultType) throws EvaluationException {
- TypedValue typedResultValue = ast.getTypedValue(new ExpressionState(context, toTypedValue(rootObject), configuration));
+ TypedValue typedResultValue = this.ast.getTypedValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration));
return ExpressionUtils.convertTypedValue(context, typedResultValue, expectedResultType);
}
@@ -125,15 +125,15 @@ public class SpelExpression implements Expression {
@Override
public Class> getValueType(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- ExpressionState eState = new ExpressionState(context, configuration);
- TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
+ ExpressionState eState = new ExpressionState(context, this.configuration);
+ TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor != null ? typeDescriptor.getType() : null;
}
@Override
public Class> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
- ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration);
- TypeDescriptor typeDescriptor = ast.getValueInternal(eState).getTypeDescriptor();
+ ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), this.configuration);
+ TypeDescriptor typeDescriptor = this.ast.getValueInternal(eState).getTypeDescriptor();
return typeDescriptor != null ? typeDescriptor.getType() : null;
}
@@ -144,61 +144,61 @@ public class SpelExpression implements Expression {
@Override
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
- ExpressionState eState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration);
- return ast.getValueInternal(eState).getTypeDescriptor();
+ ExpressionState eState = new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration);
+ return this.ast.getValueInternal(eState).getTypeDescriptor();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- ExpressionState eState = new ExpressionState(context, configuration);
- return ast.getValueInternal(eState).getTypeDescriptor();
+ ExpressionState eState = new ExpressionState(context, this.configuration);
+ return this.ast.getValueInternal(eState).getTypeDescriptor();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), configuration);
- return ast.getValueInternal(eState).getTypeDescriptor();
+ ExpressionState eState = new ExpressionState(context, toTypedValue(rootObject), this.configuration);
+ return this.ast.getValueInternal(eState).getTypeDescriptor();
}
@Override
public String getExpressionString() {
- return expression;
+ return this.expression;
}
@Override
public boolean isWritable(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- return ast.isWritable(new ExpressionState(context, configuration));
+ return this.ast.isWritable(new ExpressionState(context, this.configuration));
}
@Override
public boolean isWritable(Object rootObject) throws EvaluationException {
- return ast.isWritable(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration));
+ return this.ast.isWritable(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration));
}
@Override
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- return ast.isWritable(new ExpressionState(context, toTypedValue(rootObject), configuration));
+ return this.ast.isWritable(new ExpressionState(context, toTypedValue(rootObject), this.configuration));
}
@Override
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- ast.setValue(new ExpressionState(context, configuration), value);
+ this.ast.setValue(new ExpressionState(context, this.configuration), value);
}
@Override
public void setValue(Object rootObject, Object value) throws EvaluationException {
- ast.setValue(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), configuration), value);
+ this.ast.setValue(new ExpressionState(getEvaluationContext(), toTypedValue(rootObject), this.configuration), value);
}
@Override
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
Assert.notNull(context, "The EvaluationContext is required");
- ast.setValue(new ExpressionState(context, toTypedValue(rootObject), configuration), value);
+ this.ast.setValue(new ExpressionState(context, toTypedValue(rootObject), this.configuration), value);
}
// impl only
@@ -207,7 +207,7 @@ public class SpelExpression implements Expression {
* @return return the Abstract Syntax Tree for the expression
*/
public SpelNode getAST() {
- return ast;
+ return this.ast;
}
/**
@@ -217,7 +217,7 @@ public class SpelExpression implements Expression {
* @return the string representation of the AST
*/
public String toStringAST() {
- return ast.toStringAST();
+ return this.ast.toStringAST();
}
/**
@@ -225,10 +225,10 @@ public class SpelExpression implements Expression {
* @return the default evaluation context
*/
public EvaluationContext getEvaluationContext() {
- if (defaultContext == null) {
- defaultContext = new StandardEvaluationContext();
+ if (this.defaultContext == null) {
+ this.defaultContext = new StandardEvaluationContext();
}
- return defaultContext;
+ return this.defaultContext;
}
/**
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java
index 12b71d6b6d..69e4d3a625 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Token.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,8 @@
package org.springframework.expression.spel.standard;
/**
- * Holder for a kind of token, the associated data and its position in the input data stream (start/end).
+ * Holder for a kind of token, the associated data and its position in the input data
+ * stream (start/end).
*
* @author Andy Clement
* @since 3.0
@@ -25,12 +26,17 @@ package org.springframework.expression.spel.standard;
class Token {
TokenKind kind;
+
String data;
+
int startpos; // index of first character
+
int endpos; // index of char after the last character
+
/**
- * Constructor for use when there is no particular data for the token (eg. TRUE or '+')
+ * Constructor for use when there is no particular data for the token (eg. TRUE or
+ * '+')
* @param startpos the exact start
* @param endpos the index to the last character
*/
@@ -47,42 +53,42 @@ class Token {
public TokenKind getKind() {
- return kind;
+ return this.kind;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
- s.append("[").append(kind.toString());
- if (kind.hasPayload()) {
- s.append(":").append(data);
+ s.append("[").append(this.kind.toString());
+ if (this.kind.hasPayload()) {
+ s.append(":").append(this.data);
}
s.append("]");
- s.append("(").append(startpos).append(",").append(endpos).append(")");
+ s.append("(").append(this.startpos).append(",").append(this.endpos).append(")");
return s.toString();
}
public boolean isIdentifier() {
- return kind==TokenKind.IDENTIFIER;
+ return this.kind==TokenKind.IDENTIFIER;
}
public boolean isNumericRelationalOperator() {
- return kind==TokenKind.GT || kind==TokenKind.GE || kind==TokenKind.LT || kind==TokenKind.LE || kind==TokenKind.EQ || kind==TokenKind.NE;
+ return this.kind==TokenKind.GT || this.kind==TokenKind.GE || this.kind==TokenKind.LT || this.kind==TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE;
}
public String stringValue() {
- return data;
+ return this.data;
}
public Token asInstanceOfToken() {
- return new Token(TokenKind.INSTANCEOF,startpos,endpos);
+ return new Token(TokenKind.INSTANCEOF,this.startpos,this.endpos);
}
public Token asMatchesToken() {
- return new Token(TokenKind.MATCHES,startpos,endpos);
+ return new Token(TokenKind.MATCHES,this.startpos,this.endpos);
}
public Token asBetweenToken() {
- return new Token(TokenKind.BETWEEN,startpos,endpos);
+ return new Token(TokenKind.BETWEEN,this.startpos,this.endpos);
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java
index d0b42deea2..c50ac090ba 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,44 +17,135 @@
package org.springframework.expression.spel.standard;
/**
+ * Token Kinds.
+ *
* @author Andy Clement
* @since 3.0
*/
enum TokenKind {
+
// ordered by priority - operands first
- LITERAL_INT, LITERAL_LONG, LITERAL_HEXINT, LITERAL_HEXLONG, LITERAL_STRING, LITERAL_REAL, LITERAL_REAL_FLOAT,
- LPAREN("("), RPAREN(")"), COMMA(","), IDENTIFIER,
- COLON(":"),HASH("#"),RSQUARE("]"), LSQUARE("["),
- LCURLY("{"),RCURLY("}"),
- DOT("."), PLUS("+"), STAR("*"), MINUS("-"), SELECT_FIRST("^["), SELECT_LAST("$["), QMARK("?"), PROJECT("!["),
- DIV("/"), GE(">="), GT(">"), LE("<="), LT("<"), EQ("=="), NE("!="),
- MOD("%"), NOT("!"), ASSIGN("="), INSTANCEOF("instanceof"), MATCHES("matches"), BETWEEN("between"),
- SELECT("?["), POWER("^"),
- ELVIS("?:"), SAFE_NAVI("?."), BEAN_REF("@"), SYMBOLIC_OR("||"), SYMBOLIC_AND("&&"), INC("++"), DEC("--")
- ;
+
+ LITERAL_INT,
+
+ LITERAL_LONG,
+
+ LITERAL_HEXINT,
+
+ LITERAL_HEXLONG,
+
+ LITERAL_STRING,
+
+ LITERAL_REAL,
+
+ LITERAL_REAL_FLOAT,
+
+ LPAREN("("),
+
+ RPAREN(")"),
+
+ COMMA(","),
+
+ IDENTIFIER,
+
+ COLON(":"),
+
+ HASH("#"),
+
+ RSQUARE("]"),
+
+ LSQUARE("["),
+
+ LCURLY("{"),
+
+ RCURLY("}"),
+
+ DOT("."),
+
+ PLUS("+"),
+
+ STAR("*"),
+
+ MINUS("-"),
+
+ SELECT_FIRST("^["),
+
+ SELECT_LAST("$["),
+
+ QMARK("?"),
+
+ PROJECT("!["),
+
+ DIV("/"),
+
+ GE(">="),
+
+ GT(">"),
+
+ LE("<="),
+
+ LT("<"),
+
+ EQ("=="),
+
+ NE("!="),
+
+ MOD("%"),
+
+ NOT("!"),
+
+ ASSIGN("="),
+
+ INSTANCEOF("instanceof"),
+
+ MATCHES("matches"),
+
+ BETWEEN("between"),
+
+ SELECT("?["),
+
+ POWER("^"),
+
+ ELVIS("?:"),
+
+ SAFE_NAVI("?."),
+
+ BEAN_REF("@"),
+
+ SYMBOLIC_OR("||"),
+
+ SYMBOLIC_AND("&&"),
+
+ INC("++"),
+
+ DEC("--");
+
char[] tokenChars;
+
private boolean hasPayload; // is there more to this token than simply the kind
+
private TokenKind(String tokenString) {
- tokenChars = tokenString.toCharArray();
- hasPayload = tokenChars.length==0;
+ this.tokenChars = tokenString.toCharArray();
+ this.hasPayload = this.tokenChars.length==0;
}
private TokenKind() {
this("");
}
+
@Override
public String toString() {
- return this.name()+(tokenChars.length!=0?"("+new String(tokenChars)+")":"");
+ return this.name()+(this.tokenChars.length!=0?"("+new String(this.tokenChars)+")":"");
}
public boolean hasPayload() {
- return hasPayload;
+ return this.hasPayload;
}
public int getLength() {
- return tokenChars.length;
+ return this.tokenChars.length;
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java
index 4223a40542..cd2c6128b0 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java
@@ -34,276 +34,337 @@ import org.springframework.util.Assert;
*/
class Tokenizer {
+ // if this is changed, it must remain sorted
+ private static final String[] ALTERNATIVE_OPERATOR_NAMES = { "DIV", "EQ", "GE", "GT",
+ "LE", "LT", "MOD", "NE", "NOT" };
+
+ private static final byte FLAGS[] = new byte[256];
+
+ private static final byte IS_DIGIT = 0x01;
+
+ private static final byte IS_HEXDIGIT = 0x02;
+
+ private static final byte IS_ALPHA = 0x04;
+
+ static {
+ for (int ch = '0'; ch <= '9'; ch++) {
+ FLAGS[ch] |= IS_DIGIT | IS_HEXDIGIT;
+ }
+ for (int ch = 'A'; ch <= 'F'; ch++) {
+ FLAGS[ch] |= IS_HEXDIGIT;
+ }
+ for (int ch = 'a'; ch <= 'f'; ch++) {
+ FLAGS[ch] |= IS_HEXDIGIT;
+ }
+ for (int ch = 'A'; ch <= 'Z'; ch++) {
+ FLAGS[ch] |= IS_ALPHA;
+ }
+ for (int ch = 'a'; ch <= 'z'; ch++) {
+ FLAGS[ch] |= IS_ALPHA;
+ }
+ }
+
+
String expressionString;
+
char[] toProcess;
+
int pos;
+
int max;
+
List tokens = new ArrayList();
+
public Tokenizer(String inputdata) {
this.expressionString = inputdata;
- this.toProcess = (inputdata+"\0").toCharArray();
- this.max = toProcess.length;
+ this.toProcess = (inputdata + "\0").toCharArray();
+ this.max = this.toProcess.length;
this.pos = 0;
process();
}
+
public void process() {
- while (pos':
- if (isTwoCharToken(TokenKind.GE)) {
- pushPairToken(TokenKind.GE);
- } else {
- pushCharToken(TokenKind.GT);
- }
- break;
- case '<':
- if (isTwoCharToken(TokenKind.LE)) {
- pushPairToken(TokenKind.LE);
- } else {
- pushCharToken(TokenKind.LT);
- }
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- lexNumericLiteral(ch=='0');
- break;
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- // drift over white space
- pos++;
- break;
- case '\'':
- lexQuotedStringLiteral();
- break;
- case '"':
- lexDoubleQuotedStringLiteral();
- break;
- case 0:
- // hit sentinel at end of value
- pos++; // will take us to the end
- break;
- case '\\':
- throw new InternalParseException(new SpelParseException(expressionString,pos,SpelMessage.UNEXPECTED_ESCAPE_CHAR));
- default:
- throw new IllegalStateException("Cannot handle ("+Integer.valueOf(ch)+") '"+ch+"'");
+ break;
+ case '-':
+ if (isTwoCharToken(TokenKind.DEC)) {
+ pushPairToken(TokenKind.DEC);
+ }
+ else {
+ pushCharToken(TokenKind.MINUS);
+ }
+ break;
+ case ':':
+ pushCharToken(TokenKind.COLON);
+ break;
+ case '.':
+ pushCharToken(TokenKind.DOT);
+ break;
+ case ',':
+ pushCharToken(TokenKind.COMMA);
+ break;
+ case '*':
+ pushCharToken(TokenKind.STAR);
+ break;
+ case '/':
+ pushCharToken(TokenKind.DIV);
+ break;
+ case '%':
+ pushCharToken(TokenKind.MOD);
+ break;
+ case '(':
+ pushCharToken(TokenKind.LPAREN);
+ break;
+ case ')':
+ pushCharToken(TokenKind.RPAREN);
+ break;
+ case '[':
+ pushCharToken(TokenKind.LSQUARE);
+ break;
+ case '#':
+ pushCharToken(TokenKind.HASH);
+ break;
+ case ']':
+ pushCharToken(TokenKind.RSQUARE);
+ break;
+ case '{':
+ pushCharToken(TokenKind.LCURLY);
+ break;
+ case '}':
+ pushCharToken(TokenKind.RCURLY);
+ break;
+ case '@':
+ pushCharToken(TokenKind.BEAN_REF);
+ break;
+ case '^':
+ if (isTwoCharToken(TokenKind.SELECT_FIRST)) {
+ pushPairToken(TokenKind.SELECT_FIRST);
+ }
+ else {
+ pushCharToken(TokenKind.POWER);
+ }
+ break;
+ case '!':
+ if (isTwoCharToken(TokenKind.NE)) {
+ pushPairToken(TokenKind.NE);
+ }
+ else if (isTwoCharToken(TokenKind.PROJECT)) {
+ pushPairToken(TokenKind.PROJECT);
+ }
+ else {
+ pushCharToken(TokenKind.NOT);
+ }
+ break;
+ case '=':
+ if (isTwoCharToken(TokenKind.EQ)) {
+ pushPairToken(TokenKind.EQ);
+ }
+ else {
+ pushCharToken(TokenKind.ASSIGN);
+ }
+ break;
+ case '&':
+ if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) {
+ throw new InternalParseException(new SpelParseException(
+ this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER,
+ "&"));
+ }
+ pushPairToken(TokenKind.SYMBOLIC_AND);
+ break;
+ case '|':
+ if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) {
+ throw new InternalParseException(new SpelParseException(
+ this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER,
+ "|"));
+ }
+ pushPairToken(TokenKind.SYMBOLIC_OR);
+ break;
+ case '?':
+ if (isTwoCharToken(TokenKind.SELECT)) {
+ pushPairToken(TokenKind.SELECT);
+ }
+ else if (isTwoCharToken(TokenKind.ELVIS)) {
+ pushPairToken(TokenKind.ELVIS);
+ }
+ else if (isTwoCharToken(TokenKind.SAFE_NAVI)) {
+ pushPairToken(TokenKind.SAFE_NAVI);
+ }
+ else {
+ pushCharToken(TokenKind.QMARK);
+ }
+ break;
+ case '$':
+ if (isTwoCharToken(TokenKind.SELECT_LAST)) {
+ pushPairToken(TokenKind.SELECT_LAST);
+ }
+ else {
+ lexIdentifier();
+ }
+ break;
+ case '>':
+ if (isTwoCharToken(TokenKind.GE)) {
+ pushPairToken(TokenKind.GE);
+ }
+ else {
+ pushCharToken(TokenKind.GT);
+ }
+ break;
+ case '<':
+ if (isTwoCharToken(TokenKind.LE)) {
+ pushPairToken(TokenKind.LE);
+ }
+ else {
+ pushCharToken(TokenKind.LT);
+ }
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ lexNumericLiteral(ch == '0');
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ // drift over white space
+ this.pos++;
+ break;
+ case '\'':
+ lexQuotedStringLiteral();
+ break;
+ case '"':
+ lexDoubleQuotedStringLiteral();
+ break;
+ case 0:
+ // hit sentinel at end of value
+ this.pos++; // will take us to the end
+ break;
+ case '\\':
+ throw new InternalParseException(
+ new SpelParseException(this.expressionString, this.pos,
+ SpelMessage.UNEXPECTED_ESCAPE_CHAR));
+ default:
+ throw new IllegalStateException("Cannot handle ("
+ + Integer.valueOf(ch) + ") '" + ch + "'");
}
}
}
}
public List getTokens() {
- return tokens;
+ return this.tokens;
}
// STRING_LITERAL: '\''! (APOS|~'\'')* '\''!;
private void lexQuotedStringLiteral() {
- int start = pos;
+ int start = this.pos;
boolean terminated = false;
while (!terminated) {
- pos++;
- char ch = toProcess[pos];
- if (ch=='\'') {
+ this.pos++;
+ char ch = this.toProcess[this.pos];
+ if (ch == '\'') {
// may not be the end if the char after is also a '
- if (toProcess[pos+1]=='\'') {
- pos++; // skip over that too, and continue
- } else {
+ if (this.toProcess[this.pos + 1] == '\'') {
+ this.pos++; // skip over that too, and continue
+ }
+ else {
terminated = true;
}
}
- if (ch==0) {
- throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NON_TERMINATING_QUOTED_STRING));
+ if (ch == 0) {
+ throw new InternalParseException(new SpelParseException(this.expressionString,
+ start, SpelMessage.NON_TERMINATING_QUOTED_STRING));
}
}
- pos++;
- tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, pos));
+ this.pos++;
+ this.tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start, this.pos), start, this.pos));
}
- // DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
+ // DQ_STRING_LITERAL: '"'! (~'"')* '"'!;
private void lexDoubleQuotedStringLiteral() {
- int start = pos;
+ int start = this.pos;
boolean terminated = false;
while (!terminated) {
- pos++;
- char ch = toProcess[pos];
- if (ch=='"') {
+ this.pos++;
+ char ch = this.toProcess[this.pos];
+ if (ch == '"') {
// may not be the end if the char after is also a "
- if (toProcess[pos+1]=='"') {
- pos++; // skip over that too, and continue
- } else {
+ if (this.toProcess[this.pos + 1] == '"') {
+ this.pos++; // skip over that too, and continue
+ }
+ else {
terminated = true;
}
}
- if (ch==0) {
- throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING));
+ if (ch == 0) {
+ throw new InternalParseException(new SpelParseException(this.expressionString,
+ start, SpelMessage.NON_TERMINATING_DOUBLE_QUOTED_STRING));
}
}
- pos++;
- tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start,pos), start, pos));
+ this.pos++;
+ this.tokens.add(new Token(TokenKind.LITERAL_STRING, subarray(start, this.pos), start, this.pos));
}
-// REAL_LITERAL :
-// ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
-// ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
-// ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
-// ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
-// fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
-// fragment HEX_DIGIT : '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';
-//
-// fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* (DECIMAL_DIGIT)+ ;
-// fragment SIGN : '+' | '-' ;
-// fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';
-// INTEGER_LITERAL
-// : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
+ // REAL_LITERAL :
+ // ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
+ // ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
+ // ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
+ // ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
+ // fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
+ // fragment HEX_DIGIT :
+ // '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';
+ //
+ // fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)*
+ // (DECIMAL_DIGIT)+ ;
+ // fragment SIGN : '+' | '-' ;
+ // fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';
+ // INTEGER_LITERAL
+ // : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
private void lexNumericLiteral(boolean firstCharIsZero) {
boolean isReal = false;
- int start = pos;
- char ch = toProcess[pos+1];
- boolean isHex = ch=='x' || ch=='X';
+ int start = this.pos;
+ char ch = this.toProcess[this.pos + 1];
+ boolean isHex = ch == 'x' || ch == 'X';
// deal with hexadecimal
if (firstCharIsZero && isHex) {
- pos=pos+1;
+ this.pos = this.pos + 1;
do {
- pos++;
- } while (isHexadecimalDigit(toProcess[pos]));
- if (isChar('L','l')) {
- pushHexIntToken(subarray(start+2,pos),true, start, pos);
- pos++;
- } else {
- pushHexIntToken(subarray(start+2,pos),false, start, pos);
+ this.pos++;
+ }
+ while (isHexadecimalDigit(this.toProcess[this.pos]));
+ if (isChar('L', 'l')) {
+ pushHexIntToken(subarray(start + 2, this.pos), true, start, this.pos);
+ this.pos++;
+ }
+ else {
+ pushHexIntToken(subarray(start + 2, this.pos), false, start, this.pos);
}
return;
}
@@ -312,134 +373,150 @@ class Tokenizer {
// Consume first part of number
do {
- pos++;
- } while (isDigit(toProcess[pos]));
+ this.pos++;
+ }
+ while (isDigit(this.toProcess[this.pos]));
// a '.' indicates this number is a real
- ch = toProcess[pos];
- if (ch=='.') {
+ ch = this.toProcess[this.pos];
+ if (ch == '.') {
isReal = true;
- int dotpos = pos;
+ int dotpos = this.pos;
// carry on consuming digits
do {
- pos++;
- } while (isDigit(toProcess[pos]));
- if (pos == dotpos + 1) {
+ this.pos++;
+ }
+ while (isDigit(this.toProcess[this.pos]));
+ if (this.pos == dotpos + 1) {
// the number is something like '3.'. It is really an int but may be
// part of something like '3.toString()'. In this case process it as
// an int and leave the dot as a separate token.
- pos = dotpos;
- pushIntToken(subarray(start, pos), false, start, pos);
+ this.pos = dotpos;
+ pushIntToken(subarray(start, this.pos), false, start, this.pos);
return;
}
}
- int endOfNumber = pos;
+ int endOfNumber = this.pos;
// Now there may or may not be an exponent
// is it a long ?
- if (isChar('L','l')) {
+ if (isChar('L', 'l')) {
if (isReal) { // 3.4L - not allowed
- throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.REAL_CANNOT_BE_LONG));
+ throw new InternalParseException(new SpelParseException(this.expressionString,
+ start, SpelMessage.REAL_CANNOT_BE_LONG));
}
pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber);
- pos++;
- } else if (isExponentChar(toProcess[pos])) {
+ this.pos++;
+ }
+ else if (isExponentChar(this.toProcess[this.pos])) {
isReal = true; // if it wasn't before, it is now
- pos++;
- char possibleSign = toProcess[pos];
+ this.pos++;
+ char possibleSign = this.toProcess[this.pos];
if (isSign(possibleSign)) {
- pos++;
+ this.pos++;
}
// exponent digits
do {
- pos++;
- } while (isDigit(toProcess[pos]));
- boolean isFloat = false;
- if (isFloatSuffix(toProcess[pos])) {
- isFloat = true;
- endOfNumber = ++pos;
- } else if (isDoubleSuffix(toProcess[pos])) {
- endOfNumber = ++pos;
+ this.pos++;
}
- pushRealToken(subarray(start,pos), isFloat, start, pos);
- } else {
- ch = toProcess[pos];
+ while (isDigit(this.toProcess[this.pos]));
+ boolean isFloat = false;
+ if (isFloatSuffix(this.toProcess[this.pos])) {
+ isFloat = true;
+ endOfNumber = ++this.pos;
+ }
+ else if (isDoubleSuffix(this.toProcess[this.pos])) {
+ endOfNumber = ++this.pos;
+ }
+ pushRealToken(subarray(start, this.pos), isFloat, start, this.pos);
+ }
+ else {
+ ch = this.toProcess[this.pos];
boolean isFloat = false;
if (isFloatSuffix(ch)) {
isReal = true;
isFloat = true;
- endOfNumber = ++pos;
- } else if (isDoubleSuffix(ch)) {
+ endOfNumber = ++this.pos;
+ }
+ else if (isDoubleSuffix(ch)) {
isReal = true;
- endOfNumber = ++pos;
+ endOfNumber = ++this.pos;
}
if (isReal) {
- pushRealToken(subarray(start,endOfNumber), isFloat, start, endOfNumber);
- } else {
- pushIntToken(subarray(start,endOfNumber), false, start, endOfNumber);
+ pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber);
+ }
+ else {
+ pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber);
}
}
}
- // if this is changed, it must remain sorted
- private static final String[] alternativeOperatorNames = { "DIV","EQ","GE","GT","LE","LT","MOD","NE","NOT"};
-
private void lexIdentifier() {
- int start = pos;
+ int start = this.pos;
do {
- pos++;
- } while (isIdentifier(toProcess[pos]));
- char[] subarray = subarray(start,pos);
+ this.pos++;
+ }
+ while (isIdentifier(this.toProcess[this.pos]));
+ char[] subarray = subarray(start, this.pos);
- // Check if this is the alternative (textual) representation of an operator (see alternativeOperatorNames)
- if ((pos-start)==2 || (pos-start)==3) {
+ // Check if this is the alternative (textual) representation of an operator (see
+ // alternativeOperatorNames)
+ if ((this.pos - start) == 2 || (this.pos - start) == 3) {
String asString = new String(subarray).toUpperCase();
- int idx = Arrays.binarySearch(alternativeOperatorNames,asString);
- if (idx>=0) {
- pushOneCharOrTwoCharToken(TokenKind.valueOf(asString),start,subarray);
+ int idx = Arrays.binarySearch(ALTERNATIVE_OPERATOR_NAMES, asString);
+ if (idx >= 0) {
+ pushOneCharOrTwoCharToken(TokenKind.valueOf(asString), start, subarray);
return;
}
}
- tokens.add(new Token(TokenKind.IDENTIFIER,subarray,start,pos));
+ this.tokens.add(new Token(TokenKind.IDENTIFIER, subarray, start, this.pos));
}
- private void pushIntToken(char[] data,boolean isLong, int start, int end) {
+ private void pushIntToken(char[] data, boolean isLong, int start, int end) {
if (isLong) {
- tokens.add(new Token(TokenKind.LITERAL_LONG,data, start, end));
- } else {
- tokens.add(new Token(TokenKind.LITERAL_INT,data, start, end));
+ this.tokens.add(new Token(TokenKind.LITERAL_LONG, data, start, end));
+ }
+ else {
+ this.tokens.add(new Token(TokenKind.LITERAL_INT, data, start, end));
}
}
- private void pushHexIntToken(char[] data,boolean isLong, int start, int end) {
- if (data.length==0) {
+ private void pushHexIntToken(char[] data, boolean isLong, int start, int end) {
+ if (data.length == 0) {
if (isLong) {
- throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NOT_A_LONG,expressionString.substring(start,end+1)));
- } else {
- throw new InternalParseException(new SpelParseException(expressionString,start,SpelMessage.NOT_AN_INTEGER,expressionString.substring(start,end)));
+ throw new InternalParseException(new SpelParseException(this.expressionString,
+ start, SpelMessage.NOT_A_LONG, this.expressionString.substring(start,
+ end + 1)));
+ }
+ else {
+ throw new InternalParseException(new SpelParseException(this.expressionString,
+ start, SpelMessage.NOT_AN_INTEGER, this.expressionString.substring(
+ start, end)));
}
}
if (isLong) {
- tokens.add(new Token(TokenKind.LITERAL_HEXLONG, data, start, end));
- } else {
- tokens.add(new Token(TokenKind.LITERAL_HEXINT, data, start, end));
+ this.tokens.add(new Token(TokenKind.LITERAL_HEXLONG, data, start, end));
+ }
+ else {
+ this.tokens.add(new Token(TokenKind.LITERAL_HEXINT, data, start, end));
}
}
private void pushRealToken(char[] data, boolean isFloat, int start, int end) {
if (isFloat) {
- tokens.add(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end));
- } else {
- tokens.add(new Token(TokenKind.LITERAL_REAL, data, start, end));
+ this.tokens.add(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end));
+ }
+ else {
+ this.tokens.add(new Token(TokenKind.LITERAL_REAL, data, start, end));
}
}
private char[] subarray(int start, int end) {
char[] result = new char[end - start];
- System.arraycopy(toProcess, start, result, 0, end - start);
+ System.arraycopy(this.toProcess, start, result, 0, end - start);
return result;
}
@@ -448,98 +525,74 @@ class Tokenizer {
*/
private boolean isTwoCharToken(TokenKind kind) {
Assert.isTrue(kind.tokenChars.length == 2);
- Assert.isTrue(toProcess[pos] == kind.tokenChars[0]);
- return toProcess[pos+1] == kind.tokenChars[1];
+ Assert.isTrue(this.toProcess[this.pos] == kind.tokenChars[0]);
+ return this.toProcess[this.pos + 1] == kind.tokenChars[1];
}
/**
* Push a token of just one character in length.
*/
private void pushCharToken(TokenKind kind) {
- tokens.add(new Token(kind,pos,pos+1));
- pos++;
+ this.tokens.add(new Token(kind, this.pos, this.pos + 1));
+ this.pos++;
}
/**
* Push a token of two characters in length.
*/
private void pushPairToken(TokenKind kind) {
- tokens.add(new Token(kind,pos,pos+2));
- pos+=2;
+ this.tokens.add(new Token(kind, this.pos, this.pos + 2));
+ this.pos += 2;
}
private void pushOneCharOrTwoCharToken(TokenKind kind, int pos, char[] data) {
- tokens.add(new Token(kind,data,pos,pos+kind.getLength()));
+ this.tokens.add(new Token(kind, data, pos, pos + kind.getLength()));
}
- // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*;
+ // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*;
private boolean isIdentifier(char ch) {
- return isAlphabetic(ch) || isDigit(ch) || ch=='_' || ch=='$';
+ return isAlphabetic(ch) || isDigit(ch) || ch == '_' || ch == '$';
}
- private boolean isChar(char a,char b) {
- char ch = toProcess[pos];
- return ch==a || ch==b;
+ private boolean isChar(char a, char b) {
+ char ch = this.toProcess[this.pos];
+ return ch == a || ch == b;
}
private boolean isExponentChar(char ch) {
- return ch=='e' || ch=='E';
+ return ch == 'e' || ch == 'E';
}
private boolean isFloatSuffix(char ch) {
- return ch=='f' || ch=='F';
+ return ch == 'f' || ch == 'F';
}
private boolean isDoubleSuffix(char ch) {
- return ch=='d' || ch=='D';
+ return ch == 'd' || ch == 'D';
}
private boolean isSign(char ch) {
- return ch=='+' || ch=='-';
+ return ch == '+' || ch == '-';
}
private boolean isDigit(char ch) {
- if (ch>255) {
+ if (ch > 255) {
return false;
}
- return (flags[ch] & IS_DIGIT)!=0;
+ return (FLAGS[ch] & IS_DIGIT) != 0;
}
private boolean isAlphabetic(char ch) {
- if (ch>255) {
+ if (ch > 255) {
return false;
}
- return (flags[ch] & IS_ALPHA)!=0;
+ return (FLAGS[ch] & IS_ALPHA) != 0;
}
private boolean isHexadecimalDigit(char ch) {
- if (ch>255) {
+ if (ch > 255) {
return false;
}
- return (flags[ch] & IS_HEXDIGIT)!=0;
+ return (FLAGS[ch] & IS_HEXDIGIT) != 0;
}
-
- private static final byte flags[] = new byte[256];
- private static final byte IS_DIGIT=0x01;
- private static final byte IS_HEXDIGIT=0x02;
- private static final byte IS_ALPHA=0x04;
-
- static {
- for (int ch='0';ch<='9';ch++) {
- flags[ch]|=IS_DIGIT | IS_HEXDIGIT;
- }
- for (int ch='A';ch<='F';ch++) {
- flags[ch]|= IS_HEXDIGIT;
- }
- for (int ch='a';ch<='f';ch++) {
- flags[ch]|= IS_HEXDIGIT;
- }
- for (int ch='A';ch<='Z';ch++) {
- flags[ch]|= IS_ALPHA;
- }
- for (int ch='a';ch<='z';ch++) {
- flags[ch]|= IS_ALPHA;
- }
- }
-
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/BooleanTypedValue.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/BooleanTypedValue.java
index 6b407e8d0b..70093eeaf6 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/BooleanTypedValue.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/BooleanTypedValue.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,12 +35,7 @@ public class BooleanTypedValue extends TypedValue {
public static BooleanTypedValue forValue(boolean b) {
- if (b) {
- return TRUE;
- }
- else {
- return FALSE;
- }
+ return (b ? TRUE : FALSE);
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java
index 68e47d8533..cce6b0c3cb 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,13 +42,15 @@ import org.springframework.util.MethodInvoker;
public class ReflectionHelper {
/**
- * Compare argument arrays and return information about whether they match. A supplied type converter
- * and conversionAllowed flag allow for matches to take into account that a type may be transformed
- * into a different type by the converter.
+ * Compare argument arrays and return information about whether they match. A supplied
+ * type converter and conversionAllowed flag allow for matches to take into account
+ * that a type may be transformed into a different type by the converter.
* @param expectedArgTypes the array of types the method/constructor is expecting
- * @param suppliedArgTypes the array of types that are being supplied at the point of invocation
+ * @param suppliedArgTypes the array of types that are being supplied at the point of
+ * invocation
* @param typeConverter a registered type converter
- * @return a MatchInfo object indicating what kind of match it was or null if it was not a match
+ * @return a MatchInfo object indicating what kind of match it was or null if it was
+ * not a match
*/
static ArgumentsMatchInfo compareArguments(
List expectedArgTypes, List suppliedArgTypes, TypeConverter typeConverter) {
@@ -199,6 +201,7 @@ public class ReflectionHelper {
}
}
}
+
// If already confirmed it cannot be a match, then return
if (match == null) {
return null;
@@ -381,57 +384,74 @@ public class ReflectionHelper {
// method the arguments should have been converted to the box form of the required type.
Class> componentType = requiredParameterTypes[parameterCount-1].getComponentType();
if (componentType.isPrimitive()) {
- if (componentType==Integer.TYPE) {
- int[] repackagedArguments = (int[]) Array.newInstance(componentType, arraySize);
+ if (componentType == Integer.TYPE) {
+ int[] repackagedArguments = (int[]) Array.newInstance(componentType,
+ arraySize);
for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Integer)args[parameterCount + i - 1]).intValue();
- }
- newArgs[newArgs.length - 1] = repackagedArguments;
- } else if(componentType==Float.TYPE) {
- float[] repackagedArguments = (float[]) Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Float)args[parameterCount + i - 1]).floatValue();
- }
- newArgs[newArgs.length - 1] = repackagedArguments;
- } else if(componentType==Double.TYPE) {
- double[] repackagedArguments = (double[]) Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Double)args[parameterCount + i - 1]).doubleValue();
- }
- newArgs[newArgs.length - 1] = repackagedArguments;
- } else if(componentType==Short.TYPE) {
- short[] repackagedArguments = (short[]) Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Short)args[parameterCount + i - 1]).shortValue();
- }
- newArgs[newArgs.length - 1] = repackagedArguments;
- } else if(componentType==Character.TYPE) {
- char[] repackagedArguments = (char[]) Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Character)args[parameterCount + i - 1]).charValue();
- }
- newArgs[newArgs.length - 1] = repackagedArguments;
- } else if(componentType==Byte.TYPE) {
- byte[] repackagedArguments = (byte[]) Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Byte)args[parameterCount + i - 1]).byteValue();
- }
- newArgs[newArgs.length - 1] = repackagedArguments;
- } else if(componentType==Boolean.TYPE) {
- boolean[] repackagedArguments = (boolean[]) Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Boolean)args[parameterCount + i - 1]).booleanValue();
- }
- newArgs[newArgs.length - 1] = repackagedArguments;
- } else if(componentType==Long.TYPE) {
- long[] repackagedArguments = (long[]) Array.newInstance(componentType, arraySize);
- for (int i = 0; i < arraySize; i++) {
- repackagedArguments[i] = ((Long)args[parameterCount + i - 1]).longValue();
+ repackagedArguments[i] = ((Integer) args[parameterCount + i - 1]).intValue();
}
newArgs[newArgs.length - 1] = repackagedArguments;
}
- } else {
- Object[] repackagedArguments = (Object[]) Array.newInstance(componentType, arraySize);
+ else if (componentType == Float.TYPE) {
+ float[] repackagedArguments = (float[]) Array.newInstance(
+ componentType, arraySize);
+ for (int i = 0; i < arraySize; i++) {
+ repackagedArguments[i] = ((Float) args[parameterCount + i - 1]).floatValue();
+ }
+ newArgs[newArgs.length - 1] = repackagedArguments;
+ }
+ else if (componentType == Double.TYPE) {
+ double[] repackagedArguments = (double[]) Array.newInstance(
+ componentType, arraySize);
+ for (int i = 0; i < arraySize; i++) {
+ repackagedArguments[i] = ((Double) args[parameterCount + i - 1]).doubleValue();
+ }
+ newArgs[newArgs.length - 1] = repackagedArguments;
+ }
+ else if (componentType == Short.TYPE) {
+ short[] repackagedArguments = (short[]) Array.newInstance(
+ componentType, arraySize);
+ for (int i = 0; i < arraySize; i++) {
+ repackagedArguments[i] = ((Short) args[parameterCount + i - 1]).shortValue();
+ }
+ newArgs[newArgs.length - 1] = repackagedArguments;
+ }
+ else if (componentType == Character.TYPE) {
+ char[] repackagedArguments = (char[]) Array.newInstance(
+ componentType, arraySize);
+ for (int i = 0; i < arraySize; i++) {
+ repackagedArguments[i] = ((Character) args[parameterCount + i - 1]).charValue();
+ }
+ newArgs[newArgs.length - 1] = repackagedArguments;
+ }
+ else if (componentType == Byte.TYPE) {
+ byte[] repackagedArguments = (byte[]) Array.newInstance(
+ componentType, arraySize);
+ for (int i = 0; i < arraySize; i++) {
+ repackagedArguments[i] = ((Byte) args[parameterCount + i - 1]).byteValue();
+ }
+ newArgs[newArgs.length - 1] = repackagedArguments;
+ }
+ else if (componentType == Boolean.TYPE) {
+ boolean[] repackagedArguments = (boolean[]) Array.newInstance(
+ componentType, arraySize);
+ for (int i = 0; i < arraySize; i++) {
+ repackagedArguments[i] = ((Boolean) args[parameterCount + i - 1]).booleanValue();
+ }
+ newArgs[newArgs.length - 1] = repackagedArguments;
+ }
+ else if (componentType == Long.TYPE) {
+ long[] repackagedArguments = (long[]) Array.newInstance(
+ componentType, arraySize);
+ for (int i = 0; i < arraySize; i++) {
+ repackagedArguments[i] = ((Long) args[parameterCount + i - 1]).longValue();
+ }
+ newArgs[newArgs.length - 1] = repackagedArguments;
+ }
+ }
+ else {
+ Object[] repackagedArguments = (Object[]) Array.newInstance(
+ componentType, arraySize);
// Copy all but the varargs arguments
for (int i = 0; i < arraySize; i++) {
repackagedArguments[i] = args[parameterCount + i - 1];
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java
index e2601c893b..46202c5274 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,8 @@ import org.springframework.expression.TypedValue;
import org.springframework.util.ReflectionUtils;
/**
- * A simple ConstructorExecutor implementation that runs a constructor using reflective invocation.
+ * A simple ConstructorExecutor implementation that runs a constructor using reflective
+ * invocation.
*
* @author Andy Clement
* @author Juergen Hoeller
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorResolver.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorResolver.java
index 36d48cbf87..3d26c61c49 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorResolver.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,8 @@ import org.springframework.expression.EvaluationException;
import org.springframework.expression.TypeConverter;
/**
- * A constructor resolver that uses reflection to locate the constructor that should be invoked
+ * A constructor resolver that uses reflection to locate the constructor that should be
+ * invoked
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -43,10 +44,12 @@ public class ReflectiveConstructorResolver implements ConstructorResolver {
/**
* Locate a constructor on the type. There are three kinds of match that might occur:
*
- * An exact match where the types of the arguments match the types of the constructor
- * An in-exact match where the types we are looking for are subtypes of those defined on the constructor
- * A match where we are able to convert the arguments into those expected by the constructor, according to the
- * registered type converter.
+ * An exact match where the types of the arguments match the types of the
+ * constructor
+ * An in-exact match where the types we are looking for are subtypes of those
+ * defined on the constructor
+ * A match where we are able to convert the arguments into those expected by the
+ * constructor, according to the registered type converter.
*
*/
@Override
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java
index fe2f5aa1d1..eb195f12e2 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java
@@ -42,8 +42,8 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
/**
- * Reflection-based {@link MethodResolver} used by default in {@link StandardEvaluationContext}
- * unless explicit method resolvers have been specified.
+ * Reflection-based {@link MethodResolver} used by default in
+ * {@link StandardEvaluationContext} unless explicit method resolvers have been specified.
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -53,8 +53,6 @@ import org.springframework.expression.spel.SpelMessage;
*/
public class ReflectiveMethodResolver implements MethodResolver {
- private static Method[] NO_METHODS = new Method[0];
-
private Map, MethodFilter> filters = null;
// Using distance will ensure a more accurate match is discovered,
@@ -78,6 +76,7 @@ public class ReflectiveMethodResolver implements MethodResolver {
this.useDistance = useDistance;
}
+
/**
* Locate a method on a type. There are three kinds of match that might occur:
*
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java
index ca38218661..ddf3ad8631 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java
@@ -38,9 +38,9 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
- * Simple PropertyAccessor that uses reflection to access properties for reading and writing.
- * A property can be accessed if it is accessible as a field on the object or through a
- * getter (if being read) or a setter (if being written).
+ * Simple PropertyAccessor that uses reflection to access properties for reading and
+ * writing. A property can be accessed if it is accessible as a field on the object or
+ * through a getter (if being read) or a setter (if being written).
*
* @author Andy Clement
* @author Juergen Hoeller
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java
index ce03b4927c..7f11c0d7b3 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -242,8 +242,8 @@ public class StandardEvaluationContext implements EvaluationContext {
*/
public void registerMethodFilter(Class> type, MethodFilter filter) throws IllegalStateException {
ensureMethodResolversInitialized();
- if (reflectiveMethodResolver != null) {
- reflectiveMethodResolver.registerMethodFilter(type, filter);
+ if (this.reflectiveMethodResolver != null) {
+ this.reflectiveMethodResolver.registerMethodFilter(type, filter);
} else {
throw new IllegalStateException("Method filter cannot be set as the reflective method resolver is not in use");
}
@@ -272,7 +272,7 @@ public class StandardEvaluationContext implements EvaluationContext {
private synchronized void initializeMethodResolvers() {
if (this.methodResolvers == null) {
List defaultResolvers = new ArrayList();
- defaultResolvers.add(reflectiveMethodResolver = new ReflectiveMethodResolver());
+ defaultResolvers.add(this.reflectiveMethodResolver = new ReflectiveMethodResolver());
this.methodResolvers = defaultResolvers;
}
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java
index 92aafb28bc..fad32af97d 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,7 +21,8 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
/**
- * A simple basic TypeComparator implementation. It supports comparison of numbers and types implementing Comparable.
+ * A simple basic TypeComparator implementation. It supports comparison of numbers and
+ * types implementing Comparable.
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -35,7 +36,8 @@ public class StandardTypeComparator implements TypeComparator {
// If one is null, check if the other is
if (left == null) {
return right == null ? 0 : -1;
- } else if (right == null) {
+ }
+ else if (right == null) {
return 1; // left cannot be null
}
@@ -46,20 +48,24 @@ public class StandardTypeComparator implements TypeComparator {
if (leftNumber instanceof Double || rightNumber instanceof Double) {
double d1 = leftNumber.doubleValue();
double d2 = rightNumber.doubleValue();
- return Double.compare(d1,d2);
- } else if (leftNumber instanceof Float || rightNumber instanceof Float) {
+ return Double.compare(d1, d2);
+ }
+
+ if (leftNumber instanceof Float || rightNumber instanceof Float) {
float f1 = leftNumber.floatValue();
float f2 = rightNumber.floatValue();
- return Float.compare(f1,f2);
- } else if (leftNumber instanceof Long || rightNumber instanceof Long) {
+ return Float.compare(f1, f2);
+ }
+
+ if (leftNumber instanceof Long || rightNumber instanceof Long) {
Long l1 = leftNumber.longValue();
Long l2 = rightNumber.longValue();
return l1.compareTo(l2);
- } else {
- Integer i1 = leftNumber.intValue();
- Integer i2 = rightNumber.intValue();
- return i1.compareTo(i2);
}
+
+ Integer i1 = leftNumber.intValue();
+ Integer i2 = rightNumber.intValue();
+ return i1.compareTo(i2);
}
try {
@@ -78,12 +84,15 @@ public class StandardTypeComparator implements TypeComparator {
if (left == null || right == null) {
return true;
}
+
if (left instanceof Number && right instanceof Number) {
return true;
}
+
if (left instanceof Comparable) {
return true;
}
+
return false;
}
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
index 0500dde5d5..2104b058c3 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,8 +27,8 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.Assert;
/**
- * Default implementation of the {@link TypeConverter} interface,
- * delegating to a core Spring {@link ConversionService}.
+ * Default implementation of the {@link TypeConverter} interface, delegating to a core
+ * Spring {@link ConversionService}.
*
* @author Juergen Hoeller
* @author Andy Clement
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java
index 319985fe8a..8fea968184 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeLocator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,8 +27,9 @@ import org.springframework.expression.spel.SpelMessage;
import org.springframework.util.ClassUtils;
/**
- * A default implementation of a TypeLocator that uses the context classloader (or any classloader set upon it). It
- * supports 'well known' packages so if a type cannot be found it will try the registered imports to locate it.
+ * A default implementation of a TypeLocator that uses the context classloader (or any
+ * classloader set upon it). It supports 'well known' packages so if a type cannot be
+ * found it will try the registered imports to locate it.
*
* @author Andy Clement
* @author Juergen Hoeller
@@ -36,7 +37,7 @@ import org.springframework.util.ClassUtils;
*/
public class StandardTypeLocator implements TypeLocator {
- private ClassLoader loader;
+ private final ClassLoader loader;
private final List knownPackagePrefixes = new ArrayList();
diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/CachedMethodExecutorTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/CachedMethodExecutorTests.java
new file mode 100644
index 0000000000..75e76a5903
--- /dev/null
+++ b/spring-expression/src/test/java/org/springframework/expression/spel/CachedMethodExecutorTests.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002-2012 the original author 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.junit.Before;
+import org.junit.Test;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.ast.MethodReference;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test for caching in {@link MethodReference} (SPR-10657).
+ *
+ * @author Oliver Becker
+ */
+public class CachedMethodExecutorTests {
+
+ private final ExpressionParser parser = new SpelExpressionParser();
+
+ private StandardEvaluationContext context;
+
+
+ @Before
+ public void setUp() throws Exception {
+ this.context = new StandardEvaluationContext(new RootObject());
+ }
+
+
+ @Test
+ public void testCachedExecution() throws Exception {
+ Expression expression = this.parser.parseExpression("echo(#something)");
+
+ assertMethodExecution(expression, 42, "int: 42");
+ assertMethodExecution(expression, 42, "int: 42");
+ assertMethodExecution(expression, "Deep Thought", "String: Deep Thought");
+ assertMethodExecution(expression, 42, "int: 42");
+ }
+
+ private void assertMethodExecution(Expression expression, Object var, String expected) {
+ this.context.setVariable("something", var);
+ assertEquals(expected, expression.getValue(this.context));
+ }
+
+
+ public static class RootObject {
+
+ public String echo(String value) {
+ return "String: " + value;
+ }
+
+ public String echo(int value) {
+ return "int: " + value;
+ }
+
+ }
+
+}
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
index 89092f58ea..78e0a92fdc 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
@@ -20,6 +20,7 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+import java.sql.BatchUpdateException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -34,6 +35,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
@@ -49,6 +51,7 @@ import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.util.Assert;
import org.springframework.util.LinkedCaseInsensitiveMap;
+import org.springframework.util.StringUtils;
/**
* This is the central class in the JDBC core package.
@@ -544,21 +547,42 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
@Override
public int[] batchUpdate(final String[] sql) throws DataAccessException {
+
Assert.notEmpty(sql, "SQL array must not be empty");
+
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL batch update of " + sql.length + " statements");
}
+
class BatchUpdateStatementCallback implements StatementCallback, SqlProvider {
+
private String currSql;
+
@Override
public int[] doInStatement(Statement stmt) throws SQLException, DataAccessException {
+
int[] rowsAffected = new int[sql.length];
+
if (JdbcUtils.supportsBatchUpdates(stmt.getConnection())) {
for (String sqlStmt : sql) {
- this.currSql = sqlStmt;
+ this.currSql = appendSql(this.currSql, sqlStmt);
stmt.addBatch(sqlStmt);
}
- rowsAffected = stmt.executeBatch();
+ try {
+ rowsAffected = stmt.executeBatch();
+ }
+ catch (BatchUpdateException ex) {
+ String batchExceptionSql = null;
+ for (int i = 0; i < ex.getUpdateCounts().length; i++) {
+ if (ex.getUpdateCounts()[i] == Statement.EXECUTE_FAILED) {
+ batchExceptionSql = appendSql(batchExceptionSql, sql[i]);
+ }
+ }
+ if (StringUtils.hasLength(batchExceptionSql)) {
+ this.currSql = batchExceptionSql;
+ }
+ throw ex;
+ }
}
else {
for (int i = 0; i < sql.length; i++) {
@@ -573,6 +597,11 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
}
return rowsAffected;
}
+
+ private String appendSql(String sql, String statement) {
+ return (StringUtils.isEmpty(sql) ? statement : sql + "; " + statement);
+ }
+
@Override
public String getSql() {
return this.currSql;
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java
index ffa2d74ddf..e8d9e19c51 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java
@@ -16,6 +16,7 @@
package org.springframework.jdbc.core;
+import java.sql.BatchUpdateException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
@@ -458,6 +459,24 @@ public class JdbcTemplateTests {
verify(this.connection, atLeastOnce()).close();
}
+ @Test
+ public void testBatchUpdateWithBatchFailure() throws Exception {
+ final String[] sql = {"A", "B", "C", "D"};
+ given(this.statement.executeBatch()).willThrow(
+ new BatchUpdateException(new int[] { 1, Statement.EXECUTE_FAILED, 1,
+ Statement.EXECUTE_FAILED }));
+ mockDatabaseMetaData(true);
+ given(this.connection.createStatement()).willReturn(this.statement);
+
+ JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
+ try {
+ template.batchUpdate(sql);
+ }
+ catch (UncategorizedSQLException ex) {
+ assertThat(ex.getSql(), equalTo("B; D"));
+ }
+ }
+
@Test
public void testBatchUpdateWithNoBatchSupport() throws Exception {
final String[] sql = {"UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = 1",
diff --git a/spring-orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java b/spring-orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java
index f2086038ca..296582f2eb 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jdo/JdoTransactionManager.java
@@ -154,7 +154,7 @@ public class JdoTransactionManager extends AbstractPlatformTransactionManager
* The DataSource should match the one used by the JDO PersistenceManagerFactory:
* for example, you could specify the same JNDI DataSource for both.
* If the PersistenceManagerFactory uses a DataSource as connection factory,
- * the DataSource will be autodetected: You can still explictly specify the
+ * the DataSource will be autodetected: You can still explicitly specify the
* DataSource, but you don't need to in this case.
*
A transactional JDBC Connection for this DataSource will be provided to
* application code accessing this DataSource directly via DataSourceUtils
diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java b/spring-orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java
index 49d2cea7cc..1f424977a4 100644
--- a/spring-orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java
+++ b/spring-orm/src/main/java/org/springframework/orm/jpa/JpaTransactionManager.java
@@ -216,7 +216,7 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
* The DataSource should match the one used by the JPA EntityManagerFactory:
* for example, you could specify the same JNDI DataSource for both.
*
If the EntityManagerFactory uses a known DataSource as connection factory,
- * the DataSource will be autodetected: You can still explictly specify the
+ * the DataSource will be autodetected: You can still explicitly specify the
* DataSource, but you don't need to in this case.
*
A transactional JDBC Connection for this DataSource will be provided to
* application code accessing this DataSource directly via DataSourceUtils
@@ -258,7 +258,7 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
* Set the JPA dialect to use for this transaction manager.
* Used for vendor-specific transaction management and JDBC connection exposure.
*
If the EntityManagerFactory uses a known JpaDialect, it will be autodetected:
- * You can still explictly specify the DataSource, but you don't need to in this case.
+ * You can still explicitly specify the DataSource, but you don't need to in this case.
*
The dialect object can be used to retrieve the underlying JDBC connection
* and thus allows for exposing JPA transactions as JDBC transactions.
* @see EntityManagerFactoryInfo#getJpaDialect()
diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java
index 3a7d016980..142131fcf5 100644
--- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java
+++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
+
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
@@ -188,6 +189,21 @@ public class MockHttpServletRequestBuilder implements RequestBuilder, Mergeable
return this;
}
+ /**
+ * Set the 'Accept' header to the given media type(s).
+ *
+ * @param mediaTypes one or more media types
+ */
+ public MockHttpServletRequestBuilder accept(String... mediaTypes) {
+ Assert.notEmpty(mediaTypes, "No 'Accept' media types");
+ List result = new ArrayList(mediaTypes.length);
+ for (String mediaType : mediaTypes) {
+ result.add(MediaType.parseMediaType(mediaType));
+ }
+ this.headers.set("Accept", MediaType.toString(result));
+ return this;
+ }
+
/**
* Set the request body.
*
diff --git a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java
index 5e0f5b2a1d..0fef4e43fd 100644
--- a/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java
+++ b/spring-test-mvc/src/main/java/org/springframework/test/web/servlet/result/HeaderResultMatchers.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.test.web.servlet.result;
import static org.springframework.test.util.AssertionErrors.assertEquals;
+import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;
import org.hamcrest.Matcher;
@@ -28,6 +29,7 @@ import org.springframework.test.web.servlet.ResultMatcher;
* class is usually accessed via {@link MockMvcResultMatchers#header()}.
*
* @author Rossen Stoyanchev
+ * @author Sam Brannen
* @since 3.2
*/
public class HeaderResultMatchers {
@@ -41,36 +43,45 @@ public class HeaderResultMatchers {
}
/**
- * Assert a response header with the given Hamcrest {@link Matcher}.
+ * Assert the primary value of the named response header with the given
+ * Hamcrest {@link Matcher}.
*/
public ResultMatcher string(final String name, final Matcher super String> matcher) {
return new ResultMatcher() {
+
@Override
public void match(MvcResult result) {
- assertThat("Response header", result.getResponse().getHeader(name), matcher);
+ assertThat("Response header " + name, result.getResponse().getHeader(name), matcher);
}
};
}
/**
- * Assert the primary value of a response header as a {@link String}.
+ * Assert the primary value of the named response header as a {@link String}.
*/
public ResultMatcher string(final String name, final String value) {
return new ResultMatcher() {
+
@Override
public void match(MvcResult result) {
- assertEquals("Response header", value, result.getResponse().getHeader(name));
+ assertEquals("Response header " + name, value, result.getResponse().getHeader(name));
}
};
}
/**
- * Assert the primary value of a response header as a {@link Long}.
+ * Assert the primary value of the named response header as a {@code long}.
+ *
+ * The {@link ResultMatcher} returned by this method throws an {@link AssertionError}
+ * if the response does not contain the specified header, or if the supplied
+ * {@code value} does not match the primary value.
*/
public ResultMatcher longValue(final String name, final long value) {
return new ResultMatcher() {
+
@Override
public void match(MvcResult result) {
+ assertTrue("Response does not contain header " + name, result.getResponse().containsHeader(name));
assertEquals("Response header " + name, value, Long.parseLong(result.getResponse().getHeader(name)));
}
};
diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java
index d8bfe0c780..1a3f56771c 100644
--- a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java
+++ b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/HeaderAssertionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,72 +16,154 @@
package org.springframework.test.web.servlet.samples.standalone.resultmatchers;
-import static org.hamcrest.Matchers.nullValue;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
-
-import java.util.Date;
-
import org.junit.Before;
import org.junit.Test;
import org.springframework.stereotype.Controller;
import org.springframework.test.web.Person;
import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
+
/**
* Examples of expectations on response header values.
- *
+ *
* @author Rossen Stoyanchev
+ * @author Sam Brannen
*/
public class HeaderAssertionTests {
+ private static final String EXPECTED_ASSERTION_ERROR_MSG = "Should have thrown an AssertionError";
+
+ private static final String IF_MODIFIED_SINCE = "If-Modified-Since";
+
+ private static final String LAST_MODIFIED = "Last-Modified";
+
+ private final long currentTime = System.currentTimeMillis();
+
private MockMvc mockMvc;
private PersonController personController;
+
@Before
public void setup() {
this.personController = new PersonController();
+ this.personController.setStubTimestamp(currentTime);
this.mockMvc = standaloneSetup(this.personController).build();
}
@Test
- public void testValue() throws Exception {
- long currentTime = new Date().getTime();
- this.personController.setStubTimestamp(currentTime);
- this.mockMvc.perform(get("/persons/1").header("If-Modified-Since", currentTime - (1000 * 60)))
- .andExpect(header().string("Last-Modified", String.valueOf(currentTime)));
+ public void stringWithCorrectResponseHeaderValue() throws Exception {
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
+ .andExpect(header().string(LAST_MODIFIED, String.valueOf(currentTime)));
}
@Test
- public void testLongValue() throws Exception {
- long currentTime = new Date().getTime();
- this.personController.setStubTimestamp(currentTime);
- this.mockMvc.perform(get("/persons/1").header("If-Modified-Since", currentTime - (1000 * 60)))
- .andExpect(header().longValue("Last-Modified", currentTime));
+ public void stringWithMatcherAndCorrectResponseHeaderValue() throws Exception {
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
+ .andExpect(header().string(LAST_MODIFIED, equalTo(String.valueOf(currentTime))));
}
@Test
- public void testMatcher() throws Exception {
- long currentTime = new Date().getTime();
- this.personController.setStubTimestamp(currentTime);
- this.mockMvc.perform(get("/persons/1").header("If-Modified-Since", currentTime))
- .andExpect(status().isNotModified())
- .andExpect(header().string("Last-Modified", nullValue()));
+ public void longValueWithCorrectResponseHeaderValue() throws Exception {
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
+ .andExpect(header().longValue(LAST_MODIFIED, currentTime));
}
+ @Test
+ public void stringWithMissingResponseHeader() throws Exception {
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
+ .andExpect(status().isNotModified())//
+ .andExpect(header().string(LAST_MODIFIED, (String) null));
+ }
+
+ @Test
+ public void stringWithMatcherAndMissingResponseHeader() throws Exception {
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
+ .andExpect(status().isNotModified())//
+ .andExpect(header().string(LAST_MODIFIED, nullValue()));
+ }
+
+ @Test
+ public void longValueWithMissingResponseHeader() throws Exception {
+ try {
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime))//
+ .andExpect(status().isNotModified())//
+ .andExpect(header().longValue(LAST_MODIFIED, 99L));
+
+ fail(EXPECTED_ASSERTION_ERROR_MSG);
+ }
+ catch (AssertionError e) {
+ if (EXPECTED_ASSERTION_ERROR_MSG.equals(e.getMessage())) {
+ throw e;
+ }
+ assertEquals("Response does not contain header " + LAST_MODIFIED, e.getMessage());
+ }
+ }
+
+ @Test
+ public void stringWithIncorrectResponseHeaderValue() throws Exception {
+ long unexpected = currentTime + 1;
+ assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, String.valueOf(unexpected)), unexpected);
+ }
+
+ @Test
+ public void stringWithMatcherAndIncorrectResponseHeaderValue() throws Exception {
+ long unexpected = currentTime + 1;
+ assertIncorrectResponseHeaderValue(header().string(LAST_MODIFIED, equalTo(String.valueOf(unexpected))),
+ unexpected);
+ }
+
+ @Test
+ public void longValueWithIncorrectResponseHeaderValue() throws Exception {
+ long unexpected = currentTime + 1;
+ assertIncorrectResponseHeaderValue(header().longValue(LAST_MODIFIED, unexpected), unexpected);
+ }
+
+ private void assertIncorrectResponseHeaderValue(ResultMatcher resultMatcher, long unexpected) throws Exception {
+ try {
+ this.mockMvc.perform(get("/persons/1").header(IF_MODIFIED_SINCE, currentTime - (1000 * 60)))//
+ .andExpect(resultMatcher);
+
+ fail(EXPECTED_ASSERTION_ERROR_MSG);
+ }
+ catch (AssertionError e) {
+ if (EXPECTED_ASSERTION_ERROR_MSG.equals(e.getMessage())) {
+ throw e;
+ }
+ // [SPR-10659] Ensure that the header name is included in the message
+ //
+ // We don't use assertEquals() since we cannot control the formatting
+ // produced by JUnit or Hamcrest.
+ assertMessageContains(e, "Response header " + LAST_MODIFIED);
+ assertMessageContains(e, String.valueOf(unexpected));
+ assertMessageContains(e, String.valueOf(currentTime));
+ }
+ }
+
+ private void assertMessageContains(AssertionError error, String expected) {
+ String message = error.getMessage();
+ assertTrue("Failure message should contain: " + expected, message.contains(expected));
+ }
+
+
+ // -------------------------------------------------------------------------
@Controller
private static class PersonController {
private long timestamp;
+
public void setStubTimestamp(long timestamp) {
this.timestamp = timestamp;
}
diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java
index 533d52064a..8659f78806 100644
--- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java
+++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java
@@ -36,6 +36,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
@@ -248,8 +249,8 @@ public class MockHttpServletRequest implements HttpServletRequest {
*/
public MockHttpServletRequest(ServletContext servletContext, String method, String requestURI) {
this.servletContext = (servletContext != null ? servletContext : new MockServletContext());
- this.method = method;
- this.requestURI = requestURI;
+ this.method = (method == null ? "" : method);
+ this.requestURI = (requestURI == null ? "" : requestURI);
this.locales.add(Locale.ENGLISH);
}
@@ -858,7 +859,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
public void setMethod(String method) {
- this.method = method;
+ this.method = (method == null ? "" : method);
}
@Override
@@ -936,7 +937,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
public void setRequestURI(String requestURI) {
- this.requestURI = requestURI;
+ this.requestURI = (requestURI == null ? "" : requestURI);
}
@Override
@@ -946,8 +947,13 @@ public class MockHttpServletRequest implements HttpServletRequest {
@Override
public StringBuffer getRequestURL() {
- StringBuffer url = new StringBuffer(this.scheme);
- url.append("://").append(this.serverName).append(':').append(this.serverPort);
+ StringBuffer url = new StringBuffer(this.scheme).append("://").append(this.serverName);
+
+ if (this.serverPort > 0
+ && (("http".equalsIgnoreCase(scheme) && this.serverPort != 80) || ("https".equalsIgnoreCase(scheme) && this.serverPort != 443))) {
+ url.append(':').append(this.serverPort);
+ }
+
url.append(getRequestURI());
return url;
}
diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java
index 5daf16d36e..513f64ad56 100644
--- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java
+++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java
@@ -102,6 +102,7 @@ public class MockHttpSession implements HttpSession {
@Override
public long getCreationTime() {
+ assertIsValid();
return this.creationTime;
}
@@ -117,6 +118,7 @@ public class MockHttpSession implements HttpSession {
@Override
public long getLastAccessedTime() {
+ assertIsValid();
return this.lastAccessedTime;
}
@@ -142,6 +144,7 @@ public class MockHttpSession implements HttpSession {
@Override
public Object getAttribute(String name) {
+ assertIsValid();
Assert.notNull(name, "Attribute name must not be null");
return this.attributes.get(name);
}
@@ -153,16 +156,19 @@ public class MockHttpSession implements HttpSession {
@Override
public Enumeration getAttributeNames() {
+ assertIsValid();
return Collections.enumeration(new LinkedHashSet(this.attributes.keySet()));
}
@Override
public String[] getValueNames() {
+ assertIsValid();
return this.attributes.keySet().toArray(new String[this.attributes.size()]);
}
@Override
public void setAttribute(String name, Object value) {
+ assertIsValid();
Assert.notNull(name, "Attribute name must not be null");
if (value != null) {
this.attributes.put(name, value);
@@ -182,6 +188,7 @@ public class MockHttpSession implements HttpSession {
@Override
public void removeAttribute(String name) {
+ assertIsValid();
Assert.notNull(name, "Attribute name must not be null");
Object value = this.attributes.remove(name);
if (value instanceof HttpSessionBindingListener) {
@@ -216,11 +223,7 @@ public class MockHttpSession implements HttpSession {
*/
@Override
public void invalidate() {
- if (this.invalid) {
- throw new IllegalStateException("The session has already been invalidated");
- }
-
- // else
+ assertIsValid();
this.invalid = true;
clearAttributes();
}
@@ -229,12 +232,25 @@ public class MockHttpSession implements HttpSession {
return this.invalid;
}
+ /**
+ * Convenience method for asserting that this session has not been
+ * {@linkplain #invalidate() invalidated}.
+ *
+ * @throws IllegalStateException if this session has been invalidated
+ */
+ private void assertIsValid() {
+ if (isInvalid()) {
+ throw new IllegalStateException("The session has already been invalidated");
+ }
+ }
+
public void setNew(boolean value) {
this.isNew = value;
}
@Override
public boolean isNew() {
+ assertIsValid();
return this.isNew;
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
index 39bfb5d3cd..0b76fa4231 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ import java.lang.annotation.Target;
* @see SmartContextLoader
* @see MergedContextConfiguration
* @see ContextConfiguration
+ * @see ActiveProfilesResolver
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.annotation.Profile
*/
@@ -47,8 +48,8 @@ public @interface ActiveProfiles {
* Alias for {@link #profiles}.
*
* This attribute may not be used in conjunction
- * with {@link #profiles}, but it may be used instead of
- * {@link #profiles}.
+ * with {@link #profiles} or {@link #resolver}, but it may be used
+ * instead of them.
*/
String[] value() default {};
@@ -56,11 +57,24 @@ public @interface ActiveProfiles {
* The bean definition profiles to activate.
*
*
This attribute may not be used in conjunction
- * with {@link #value}, but it may be used instead of
- * {@link #value}.
+ * with {@link #value} or {@link #resolver}, but it may be used
+ * instead of them.
*/
String[] profiles() default {};
+ /**
+ * The type of {@link ActiveProfilesResolver} to use for resolving the active
+ * bean definition profiles programmatically.
+ *
+ *
This attribute may not be used in conjunction
+ * with {@link #profiles} or {@link #value}, but it may be used instead
+ * of them in order to resolve the active profiles programmatically.
+ *
+ * @since 4.0
+ * @see ActiveProfilesResolver
+ */
+ Class extends ActiveProfilesResolver> resolver() default ActiveProfilesResolver.class;
+
/**
* Whether or not bean definition profiles from superclasses should be
* inherited .
diff --git a/spring-test/src/main/java/org/springframework/test/context/ActiveProfilesResolver.java b/spring-test/src/main/java/org/springframework/test/context/ActiveProfilesResolver.java
new file mode 100644
index 0000000000..0985ebb02c
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/ActiveProfilesResolver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.test.context;
+
+/**
+ * Strategy interface for programmatically resolving which active bean
+ * definition profiles should be used when loading an
+ * {@link org.springframework.context.ApplicationContext ApplicationContext}
+ * for a test class.
+ *
+ *
A custom {@code ActiveProfilesResolver} can be registered via the
+ * {@link ActiveProfiles#resolver resolver} attribute of {@code @ActiveProfiles}.
+ *
+ *
Concrete implementations must provide a {@code public} no-args constructor.
+ *
+ * @author Sam Brannen
+ * @author Michail Nikolaev
+ * @since 4.0
+ * @see ActiveProfiles
+ */
+public interface ActiveProfilesResolver {
+
+ /**
+ * Resolve the bean definition profiles to use when loading an
+ * {@code ApplicationContext} for the given {@linkplain Class test class}.
+ *
+ * @param testClass the test class for which the profiles should be resolved;
+ * never {@code null}
+ * @return the list of bean definition profiles to use when loading the
+ * {@code ApplicationContext}; never {@code null}
+ * @see ActiveProfiles#resolver
+ * @see ActiveProfiles#inheritProfiles
+ */
+ String[] resolve(Class> testClass);
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
index a43897eb0b..92f5e2c764 100644
--- a/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/ContextLoaderUtils.java
@@ -16,11 +16,6 @@
package org.springframework.test.context;
-import static org.springframework.beans.BeanUtils.instantiateClass;
-import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClass;
-import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClassForTypes;
-import static org.springframework.core.annotation.AnnotationUtils.isAnnotationDeclaredLocally;
-
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
@@ -42,6 +37,9 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
+import static org.springframework.beans.BeanUtils.*;
+import static org.springframework.core.annotation.AnnotationUtils.*;
+
/**
* Utility methods for working with {@link ContextLoader ContextLoaders} and
* {@link SmartContextLoader SmartContextLoaders} and resolving resource locations,
@@ -49,12 +47,14 @@ import org.springframework.util.StringUtils;
* initializers.
*
* @author Sam Brannen
+ * @author Michail Nikolaev
* @since 3.1
* @see ContextLoader
* @see SmartContextLoader
* @see ContextConfiguration
* @see ContextConfigurationAttributes
* @see ActiveProfiles
+ * @see ActiveProfilesResolver
* @see ApplicationContextInitializer
* @see ContextHierarchy
* @see MergedContextConfiguration
@@ -477,24 +477,41 @@ abstract class ContextLoaderUtils {
while (declaringClass != null) {
ActiveProfiles annotation = declaringClass.getAnnotation(annotationType);
-
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @ActiveProfiles [%s] for declaring class [%s].", annotation,
declaringClass.getName()));
}
+ validateActiveProfilesConfiguration(declaringClass, annotation);
String[] profiles = annotation.profiles();
String[] valueProfiles = annotation.value();
+ Class extends ActiveProfilesResolver> resolverClass = annotation.resolver();
- if (!ObjectUtils.isEmpty(valueProfiles) && !ObjectUtils.isEmpty(profiles)) {
- String msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
- + "and 'profiles' [%s] attributes. Only one declaration of active bean "
- + "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass.getName(),
- ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
- logger.error(msg);
- throw new IllegalStateException(msg);
+ boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass);
+ boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
+
+ if (resolverDeclared) {
+ ActiveProfilesResolver resolver = null;
+ try {
+ resolver = instantiateClass(resolverClass, ActiveProfilesResolver.class);
+ }
+ catch (Exception e) {
+ String msg = String.format("Could not instantiate ActiveProfilesResolver of "
+ + "type [%s] for test class [%s].", resolverClass.getName(), declaringClass.getName());
+ logger.error(msg);
+ throw new IllegalStateException(msg, e);
+ }
+
+ profiles = resolver.resolve(declaringClass);
+ if (profiles == null) {
+ String msg = String.format(
+ "ActiveProfilesResolver [%s] returned a null array of bean definition profiles.",
+ resolverClass.getName());
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
}
- else if (!ObjectUtils.isEmpty(valueProfiles)) {
+ else if (valueDeclared) {
profiles = valueProfiles;
}
@@ -511,6 +528,43 @@ abstract class ContextLoaderUtils {
return StringUtils.toStringArray(activeProfiles);
}
+ private static void validateActiveProfilesConfiguration(Class> declaringClass, ActiveProfiles annotation) {
+ String[] valueProfiles = annotation.value();
+ String[] profiles = annotation.profiles();
+ Class extends ActiveProfilesResolver> resolverClass = annotation.resolver();
+ boolean valueDeclared = !ObjectUtils.isEmpty(valueProfiles);
+ boolean profilesDeclared = !ObjectUtils.isEmpty(profiles);
+ boolean resolverDeclared = !ActiveProfilesResolver.class.equals(resolverClass);
+
+ String msg = null;
+
+ if (valueDeclared && profilesDeclared) {
+ msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
+ + "and 'profiles' [%s] attributes. Only one declaration of active bean "
+ + "definition profiles is permitted per @ActiveProfiles annotation.", declaringClass.getName(),
+ ObjectUtils.nullSafeToString(valueProfiles), ObjectUtils.nullSafeToString(profiles));
+ }
+ else if (valueDeclared && resolverDeclared) {
+ msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'value' [%s] "
+ + "and 'resolver' [%s] attributes. Only one source of active bean "
+ + "definition profiles is permitted per @ActiveProfiles annotation, "
+ + "either declaritively or programmatically.", declaringClass.getName(),
+ ObjectUtils.nullSafeToString(valueProfiles), resolverClass.getName());
+ }
+ else if (profilesDeclared && resolverDeclared) {
+ msg = String.format("Test class [%s] has been configured with @ActiveProfiles' 'profiles' [%s] "
+ + "and 'resolver' [%s] attributes. Only one source of active bean "
+ + "definition profiles is permitted per @ActiveProfiles annotation, "
+ + "either declaritively or programmatically.", declaringClass.getName(),
+ ObjectUtils.nullSafeToString(profiles), resolverClass.getName());
+ }
+
+ if (msg != null) {
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ }
+
/**
* Build the {@link MergedContextConfiguration merged context configuration} for
* the supplied {@link Class testClass} and {@code defaultContextLoaderClassName},
diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
index 335497aa51..c1494050a2 100644
--- a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
+++ b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java
@@ -86,8 +86,7 @@ public abstract class AbstractTransactionalJUnit4SpringContextTests extends Abst
/**
* Set the {@code DataSource}, typically provided via Dependency Injection.
- *
This method also instantiates the {@link #jdbcTemplate} instance
- * variable.
+ *
This method also instantiates the {@link #jdbcTemplate} instance variable.
*/
@Autowired
public void setDataSource(DataSource dataSource) {
@@ -103,49 +102,75 @@ public abstract class AbstractTransactionalJUnit4SpringContextTests extends Abst
}
/**
- * Count the rows in the given table.
+ * Convenience method for counting the rows in the given table.
* @param tableName table name to count rows in
* @return the number of rows in the table
+ * @see JdbcTestUtils#countRowsInTable
*/
protected int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
/**
- * Count the rows in the given table, using the provided {@code WHERE} clause.
+ * Convenience method for counting the rows in the given table, using the
+ * provided {@code WHERE} clause.
*
See the Javadoc for {@link JdbcTestUtils#countRowsInTableWhere} for details.
* @param tableName the name of the table to count rows in
* @param whereClause the {@code WHERE} clause to append to the query
* @return the number of rows in the table that match the provided
* {@code WHERE} clause
* @since 3.2
+ * @see JdbcTestUtils#countRowsInTableWhere
*/
protected int countRowsInTableWhere(String tableName, String whereClause) {
return JdbcTestUtils.countRowsInTableWhere(this.jdbcTemplate, tableName, whereClause);
}
/**
- * Convenience method for deleting all rows from the specified tables. Use
- * with caution outside of a transaction!
+ * Convenience method for deleting all rows from the specified tables.
+ *
Use with caution outside of a transaction!
* @param names the names of the tables from which to delete
* @return the total number of rows deleted from all specified tables
+ * @see JdbcTestUtils#deleteFromTables
*/
protected int deleteFromTables(String... names) {
return JdbcTestUtils.deleteFromTables(this.jdbcTemplate, names);
}
/**
- * Convenience method for dropping all of the specified tables. Use
- * with caution outside of a transaction!
+ * Convenience method for deleting all rows from the given table, using the
+ * provided {@code WHERE} clause.
+ *
Use with caution outside of a transaction!
+ *
See the Javadoc for {@link JdbcTestUtils#deleteFromTableWhere} for details.
+ * @param tableName the name of the table to delete rows from
+ * @param whereClause the {@code WHERE} clause to append to the query
+ * @param args arguments to bind to the query (leaving it to the {@code
+ * PreparedStatement} to guess the corresponding SQL type); may also contain
+ * {@link org.springframework.jdbc.core.SqlParameterValue SqlParameterValue}
+ * objects which indicate not only the argument value but also the SQL type
+ * and optionally the scale.
+ * @return the number of rows deleted from the table
+ * @since 4.0
+ * @see JdbcTestUtils#deleteFromTableWhere
+ */
+ protected int deleteFromTableWhere(String tableName, String whereClause, Object... args) {
+ return JdbcTestUtils.deleteFromTableWhere(jdbcTemplate, tableName, whereClause, args);
+ }
+
+ /**
+ * Convenience method for dropping all of the specified tables.
+ *
Use with caution outside of a transaction!
* @param names the names of the tables to drop
* @since 3.2
+ * @see JdbcTestUtils#dropTables
*/
protected void dropTables(String... names) {
JdbcTestUtils.dropTables(this.jdbcTemplate, names);
}
/**
- * Execute the given SQL script. Use with caution outside of a transaction!
+ * Execute the given SQL script.
+ *
Use with caution outside of a transaction!
*
The script will normally be loaded by classpath. There should be one
* statement per line. Any semicolons will be removed. Do not use this
* method to execute DDL if you expect rollback.
@@ -154,11 +179,13 @@ public abstract class AbstractTransactionalJUnit4SpringContextTests extends Abst
* exception in the event of an error
* @throws DataAccessException if there is an error executing a statement
* and continueOnError was {@code false}
+ * @see JdbcTestUtils#executeSqlScript(JdbcTemplate, EncodedResource, boolean)
+ * @see #setSqlScriptEncoding
*/
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
Resource resource = this.applicationContext.getResource(sqlResourcePath);
- JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource,
- this.sqlScriptEncoding), continueOnError);
+ JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding),
+ continueOnError);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
index c5d7a6770b..e71b82d529 100644
--- a/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
+++ b/spring-test/src/main/java/org/springframework/test/context/testng/AbstractTransactionalTestNGSpringContextTests.java
@@ -77,8 +77,7 @@ public abstract class AbstractTransactionalTestNGSpringContextTests extends Abst
/**
* Set the {@code DataSource}, typically provided via Dependency Injection.
- *
This method also instantiates the {@link #jdbcTemplate} instance
- * variable.
+ *
This method also instantiates the {@link #jdbcTemplate} instance variable.
*/
@Autowired
public void setDataSource(DataSource dataSource) {
@@ -94,49 +93,75 @@ public abstract class AbstractTransactionalTestNGSpringContextTests extends Abst
}
/**
- * Count the rows in the given table.
+ * Convenience method for counting the rows in the given table.
* @param tableName table name to count rows in
* @return the number of rows in the table
+ * @see JdbcTestUtils#countRowsInTable
*/
protected int countRowsInTable(String tableName) {
return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName);
}
/**
- * Count the rows in the given table, using the provided {@code WHERE} clause.
+ * Convenience method for counting the rows in the given table, using the
+ * provided {@code WHERE} clause.
*
See the Javadoc for {@link JdbcTestUtils#countRowsInTableWhere} for details.
* @param tableName the name of the table to count rows in
* @param whereClause the {@code WHERE} clause to append to the query
* @return the number of rows in the table that match the provided
* {@code WHERE} clause
* @since 3.2
+ * @see JdbcTestUtils#countRowsInTableWhere
*/
protected int countRowsInTableWhere(String tableName, String whereClause) {
return JdbcTestUtils.countRowsInTableWhere(this.jdbcTemplate, tableName, whereClause);
}
/**
- * Convenience method for deleting all rows from the specified tables. Use
- * with caution outside of a transaction!
+ * Convenience method for deleting all rows from the specified tables.
+ *
Use with caution outside of a transaction!
* @param names the names of the tables from which to delete
* @return the total number of rows deleted from all specified tables
+ * @see JdbcTestUtils#deleteFromTables
*/
protected int deleteFromTables(String... names) {
return JdbcTestUtils.deleteFromTables(this.jdbcTemplate, names);
}
/**
- * Convenience method for dropping all of the specified tables. Use
- * with caution outside of a transaction!
+ * Convenience method for deleting all rows from the given table, using the
+ * provided {@code WHERE} clause.
+ *
Use with caution outside of a transaction!
+ *
See the Javadoc for {@link JdbcTestUtils#deleteFromTableWhere} for details.
+ * @param tableName the name of the table to delete rows from
+ * @param whereClause the {@code WHERE} clause to append to the query
+ * @param args arguments to bind to the query (leaving it to the {@code
+ * PreparedStatement} to guess the corresponding SQL type); may also contain
+ * {@link org.springframework.jdbc.core.SqlParameterValue SqlParameterValue}
+ * objects which indicate not only the argument value but also the SQL type
+ * and optionally the scale.
+ * @return the number of rows deleted from the table
+ * @since 4.0
+ * @see JdbcTestUtils#deleteFromTableWhere
+ */
+ protected int deleteFromTableWhere(String tableName, String whereClause, Object... args) {
+ return JdbcTestUtils.deleteFromTableWhere(jdbcTemplate, tableName, whereClause, args);
+ }
+
+ /**
+ * Convenience method for dropping all of the specified tables.
+ *
Use with caution outside of a transaction!
* @param names the names of the tables to drop
* @since 3.2
+ * @see JdbcTestUtils#dropTables
*/
protected void dropTables(String... names) {
JdbcTestUtils.dropTables(this.jdbcTemplate, names);
}
/**
- * Execute the given SQL script. Use with caution outside of a transaction!
+ * Execute the given SQL script.
+ *
Use with caution outside of a transaction!
*
The script will normally be loaded by classpath. There should be one
* statement per line. Any semicolons will be removed. Do not use this
* method to execute DDL if you expect rollback.
@@ -145,11 +170,13 @@ public abstract class AbstractTransactionalTestNGSpringContextTests extends Abst
* exception in the event of an error
* @throws DataAccessException if there is an error executing a statement
* and continueOnError was {@code false}
+ * @see JdbcTestUtils#executeSqlScript(JdbcTemplate, EncodedResource, boolean)
+ * @see #setSqlScriptEncoding
*/
protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException {
Resource resource = this.applicationContext.getResource(sqlResourcePath);
- JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource,
- this.sqlScriptEncoding), continueOnError);
+ JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding),
+ continueOnError);
}
}
diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java
index a237bb00d1..5eb2c42a62 100644
--- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java
+++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletRequestTests.java
@@ -193,6 +193,39 @@ public class MockHttpServletRequestTests {
assertEqualEnumerations(Collections.enumeration(preferredLocales), request.getLocales());
}
+ @Test
+ public void getRequestURL() {
+ request.setServerPort(8080);
+ request.setRequestURI("/path");
+ assertEquals("http://localhost:8080/path", request.getRequestURL().toString());
+
+ request.setScheme("https");
+ request.setServerName("example.com");
+ request.setServerPort(8443);
+ assertEquals("https://example.com:8443/path", request.getRequestURL().toString());
+ }
+
+ @Test
+ public void getRequestURLWithDefaults() {
+ StringBuffer requestURL = request.getRequestURL();
+ assertEquals("http://localhost", requestURL.toString());
+ }
+
+ @Test
+ public void getRequestURLWithDefaultsAndHttps() {
+ request.setScheme("https");
+ request.setServerPort(443);
+ StringBuffer requestURL = request.getRequestURL();
+ assertEquals("https://localhost", requestURL.toString());
+ }
+
+ @Test
+ public void getRequestURLWithNegativePort() {
+ request.setServerPort(-99);
+ StringBuffer requestURL = request.getRequestURL();
+ assertEquals("http://localhost", requestURL.toString());
+ }
+
private void assertEqualEnumerations(Enumeration> enum1, Enumeration> enum2) {
assertNotNull(enum1);
assertNotNull(enum2);
diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java
index 0379ea78d0..41b735c4bd 100644
--- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java
+++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import org.junit.Test;
*/
public class MockHttpSessionTests {
- private MockHttpSession session = new MockHttpSession();
+ private final MockHttpSession session = new MockHttpSession();
@Test
@@ -44,4 +44,103 @@ public class MockHttpSessionTests {
session.invalidate();
}
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void getCreationTimeOnInvalidatedSession() {
+ session.invalidate();
+ session.getCreationTime();
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void getLastAccessedTimeOnInvalidatedSession() {
+ session.invalidate();
+ session.getLastAccessedTime();
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void getAttributeOnInvalidatedSession() {
+ session.invalidate();
+ session.getAttribute("foo");
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void getAttributeNamesOnInvalidatedSession() {
+ session.invalidate();
+ session.getAttributeNames();
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void getValueOnInvalidatedSession() {
+ session.invalidate();
+ session.getValue("foo");
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void getValueNamesOnInvalidatedSession() {
+ session.invalidate();
+ session.getValueNames();
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void setAttributeOnInvalidatedSession() {
+ session.invalidate();
+ session.setAttribute("name", "value");
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void putValueOnInvalidatedSession() {
+ session.invalidate();
+ session.putValue("name", "value");
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void removeAttributeOnInvalidatedSession() {
+ session.invalidate();
+ session.removeAttribute("name");
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void removeValueOnInvalidatedSession() {
+ session.invalidate();
+ session.removeValue("name");
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void isNewOnInvalidatedSession() {
+ session.invalidate();
+ session.isNew();
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/web/multipart/MockMultipartHttpServletRequestTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequestTests.java
similarity index 89%
rename from spring-test/src/test/java/org/springframework/web/multipart/MockMultipartHttpServletRequestTests.java
rename to spring-test/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequestTests.java
index 029f584c1f..51fc9e983c 100644
--- a/spring-test/src/test/java/org/springframework/web/multipart/MockMultipartHttpServletRequestTests.java
+++ b/spring-test/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequestTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2006 the original author or authors.
+ * Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,12 +14,7 @@
* limitations under the License.
*/
-package org.springframework.web.multipart;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+package org.springframework.mock.web;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -31,10 +26,12 @@ import java.util.Map;
import java.util.Set;
import org.junit.Test;
-import org.springframework.mock.web.MockMultipartFile;
-import org.springframework.mock.web.MockMultipartHttpServletRequest;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.ObjectUtils;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartHttpServletRequest;
+
+import static org.junit.Assert.*;
/**
* @author Juergen Hoeller
diff --git a/spring-test/src/test/java/org/springframework/test/AbstractSpr3350SingleSpringContextTests.java b/spring-test/src/test/java/org/springframework/test/AbstractSpr3350SingleSpringContextTests.java
deleted file mode 100644
index ba8c81c294..0000000000
--- a/spring-test/src/test/java/org/springframework/test/AbstractSpr3350SingleSpringContextTests.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2002-2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.test;
-
-import org.springframework.tests.sample.beans.Pet;
-import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
-import org.springframework.context.ApplicationContext;
-
-/**
- * Abstract JUnit 3.8 based unit test which verifies new functionality requested
- * in SPR-3350 .
- *
- * @author Sam Brannen
- * @since 2.5
- */
-@SuppressWarnings("deprecation")
-public abstract class AbstractSpr3350SingleSpringContextTests extends AbstractDependencyInjectionSpringContextTests {
-
- private Pet cat;
-
-
- public AbstractSpr3350SingleSpringContextTests() {
- super();
- }
-
- public AbstractSpr3350SingleSpringContextTests(String name) {
- super(name);
- }
-
- public final void setCat(final Pet cat) {
- this.cat = cat;
- }
-
- /**
- * Forcing concrete subclasses to provide a config path appropriate to the
- * configured
- * {@link #createBeanDefinitionReader(org.springframework.context.support.GenericApplicationContext)
- * BeanDefinitionReader}.
- *
- * @see org.springframework.test.AbstractSingleSpringContextTests#getConfigPath()
- */
- @Override
- protected abstract String getConfigPath();
-
- /**
- *
- * Test which addresses the following issue raised in SPR-3350:
- *
- *
- * {@link AbstractSingleSpringContextTests} always uses an
- * {@link XmlBeanDefinitionReader} internally when creating the
- * {@link ApplicationContext} inside
- * {@link #createApplicationContext(String[])}. It would be nice to have the
- * bean definition reader creation in a separate method so that subclasses
- * can choose that individually without having to copy-n-paste code from
- * createApplicationContext() to do the context creation and refresh.
- * Consider JavaConfig where an Annotation based reader can be plugged in.
- *
- */
- public final void testApplicationContextNotAutoCreated() {
- assertNotNull("The cat field should have been autowired.", this.cat);
- assertEquals("Garfield", this.cat.getName());
- }
-}
diff --git a/spring-test/src/test/java/org/springframework/test/PropertiesBasedSpr3350SingleSpringContextTests-context.properties b/spring-test/src/test/java/org/springframework/test/PropertiesBasedSpr3350SingleSpringContextTests-context.properties
deleted file mode 100644
index 7b96503a3f..0000000000
--- a/spring-test/src/test/java/org/springframework/test/PropertiesBasedSpr3350SingleSpringContextTests-context.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-cat.(class)=org.springframework.tests.sample.beans.Pet
-cat.$0=Garfield
diff --git a/spring-test/src/test/java/org/springframework/test/PropertiesBasedSpr3350SingleSpringContextTests.java b/spring-test/src/test/java/org/springframework/test/PropertiesBasedSpr3350SingleSpringContextTests.java
deleted file mode 100644
index 07b0cf4b5e..0000000000
--- a/spring-test/src/test/java/org/springframework/test/PropertiesBasedSpr3350SingleSpringContextTests.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2002-2012 the original author 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.test;
-
-import org.springframework.beans.factory.support.BeanDefinitionReader;
-import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader;
-import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
-import org.springframework.context.support.GenericApplicationContext;
-
-/**
- * Concrete implementation of {@link AbstractSpr3350SingleSpringContextTests}
- * which configures a {@link PropertiesBeanDefinitionReader} instead of the
- * default {@link XmlBeanDefinitionReader}.
- *
- * @author Sam Brannen
- * @since 2.5
- */
-public class PropertiesBasedSpr3350SingleSpringContextTests extends AbstractSpr3350SingleSpringContextTests {
-
- public PropertiesBasedSpr3350SingleSpringContextTests() {
- super();
- }
-
- public PropertiesBasedSpr3350SingleSpringContextTests(String name) {
- super(name);
- }
-
- /**
- * Creates a new {@link PropertiesBeanDefinitionReader}.
- *
- * @see org.springframework.test.AbstractSingleSpringContextTests#createBeanDefinitionReader(org.springframework.context.support.GenericApplicationContext)
- */
- @Override
- protected final BeanDefinitionReader createBeanDefinitionReader(GenericApplicationContext context) {
- return new PropertiesBeanDefinitionReader(context);
- }
-
- /**
- * Returns
- * "PropertiesBasedSpr3350SingleSpringContextTests-context.properties".
- */
- @Override
- protected final String getConfigPath() {
- return "PropertiesBasedSpr3350SingleSpringContextTests-context.properties";
- }
-}
diff --git a/spring-test/src/test/java/org/springframework/test/Spr3264DependencyInjectionSpringContextTests.java b/spring-test/src/test/java/org/springframework/test/Spr3264DependencyInjectionSpringContextTests.java
deleted file mode 100644
index 08523c7949..0000000000
--- a/spring-test/src/test/java/org/springframework/test/Spr3264DependencyInjectionSpringContextTests.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2002-2012 the original author 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.test;
-
-/**
- * JUnit 3.8 based unit test which verifies new functionality requested in SPR-3264 .
- *
- * @author Sam Brannen
- * @since 2.5
- * @see Spr3264SingleSpringContextTests
- */
-@SuppressWarnings("deprecation")
-public class Spr3264DependencyInjectionSpringContextTests extends AbstractDependencyInjectionSpringContextTests {
-
- public Spr3264DependencyInjectionSpringContextTests() {
- super();
- }
-
- public Spr3264DependencyInjectionSpringContextTests(String name) {
- super(name);
- }
-
- /**
- *
- * Test which addresses the following issue raised in SPR-3264:
- *
- *
- * AbstractDependencyInjectionSpringContextTests will try to apply
- * auto-injection; this can be disabled but it has to be done manually
- * inside the onSetUp...
- *
- */
- public void testInjectDependenciesThrowsIllegalStateException() {
-
- // Re-assert issues covered by Spr3264SingleSpringContextTests as a
- // safety net.
- assertNull("The ApplicationContext should NOT be automatically created if no 'locations' are defined.",
- this.applicationContext);
- assertEquals("Verifying the ApplicationContext load count.", 0, super.getLoadCount());
-
- // Assert changes to AbstractDependencyInjectionSpringContextTests:
- new AssertThrows(IllegalStateException.class) {
-
- @Override
- public void test() throws Exception {
- Spr3264DependencyInjectionSpringContextTests.super.injectDependencies();
- }
- }.runTest();
- }
-
-}
diff --git a/spring-test/src/test/java/org/springframework/test/Spr3264SingleSpringContextTests.java b/spring-test/src/test/java/org/springframework/test/Spr3264SingleSpringContextTests.java
deleted file mode 100644
index 4b3223f4ba..0000000000
--- a/spring-test/src/test/java/org/springframework/test/Spr3264SingleSpringContextTests.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2002-2012 the original author 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.test;
-
-/**
- * JUnit 3.8 based unit test which verifies new functionality requested in SPR-3264 .
- *
- * @author Sam Brannen
- * @since 2.5
- * @see Spr3264DependencyInjectionSpringContextTests
- */
-@SuppressWarnings("deprecation")
-public class Spr3264SingleSpringContextTests extends AbstractSingleSpringContextTests {
-
- public Spr3264SingleSpringContextTests() {
- super();
- }
-
- public Spr3264SingleSpringContextTests(String name) {
- super(name);
- }
-
- /**
- *
- * Test which addresses the following issue raised in SPR-3264:
- *
- *
- * AbstractSingleSpringContextTests always expects an application context to
- * be created even if no files/locations are specified which can lead to NPE
- * problems or force an appCtx to be instantiated even if not needed.
- *
- */
- public void testApplicationContextNotAutoCreated() {
- assertNull("The ApplicationContext should NOT be automatically created if no 'locations' are defined.",
- super.applicationContext);
- assertEquals("Verifying the ApplicationContext load count.", 0, super.getLoadCount());
- }
-
-}
diff --git a/spring-test/src/test/java/org/springframework/test/XmlBasedSpr3350SingleSpringContextTests-context.xml b/spring-test/src/test/java/org/springframework/test/XmlBasedSpr3350SingleSpringContextTests-context.xml
deleted file mode 100644
index b1939de12f..0000000000
--- a/spring-test/src/test/java/org/springframework/test/XmlBasedSpr3350SingleSpringContextTests-context.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/spring-test/src/test/java/org/springframework/test/XmlBasedSpr3350SingleSpringContextTests.java b/spring-test/src/test/java/org/springframework/test/XmlBasedSpr3350SingleSpringContextTests.java
deleted file mode 100644
index 192d005f4a..0000000000
--- a/spring-test/src/test/java/org/springframework/test/XmlBasedSpr3350SingleSpringContextTests.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2002-2012 the original author 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.test;
-
-import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
-
-/**
- * Concrete implementation of {@link AbstractSpr3350SingleSpringContextTests}
- * which is based on the default {@link XmlBeanDefinitionReader}.
- *
- * @author Sam Brannen
- * @since 2.5
- */
-public class XmlBasedSpr3350SingleSpringContextTests extends AbstractSpr3350SingleSpringContextTests {
-
- public XmlBasedSpr3350SingleSpringContextTests() {
- super();
- }
-
- public XmlBasedSpr3350SingleSpringContextTests(String name) {
- super(name);
- }
-
- /**
- * Returns "XmlBasedSpr3350SingleSpringContextTests-context.xml".
- */
- @Override
- protected final String getConfigPath() {
- return "XmlBasedSpr3350SingleSpringContextTests-context.xml";
- }
-}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests$BareAnnotations-context.xml b/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests$BareAnnotations-context.xml
similarity index 100%
rename from spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests$BareAnnotations-context.xml
rename to spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests$BareAnnotations-context.xml
diff --git a/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java
new file mode 100644
index 0000000000..6942771cfe
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/AbstractContextLoaderUtilsTests.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.test.context;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+
+import static org.junit.Assert.*;
+
+/**
+ * Abstract base class for tests involving {@link ContextLoaderUtils}.
+ *
+ * @author Sam Brannen
+ * @since 3.1
+ */
+abstract class AbstractContextLoaderUtilsTests {
+
+ static final Class>[] EMPTY_CLASS_ARRAY = new Class>[0];
+ static final String[] EMPTY_STRING_ARRAY = new String[0];
+ static final Set>> EMPTY_INITIALIZER_CLASSES = //
+ Collections.>> emptySet();
+
+
+ void assertMergedConfig(MergedContextConfiguration mergedConfig, Class> expectedTestClass,
+ String[] expectedLocations, Class>[] expectedClasses,
+ Class extends ContextLoader> expectedContextLoaderClass) {
+ assertMergedConfig(mergedConfig, expectedTestClass, expectedLocations, expectedClasses,
+ EMPTY_INITIALIZER_CLASSES, expectedContextLoaderClass);
+ }
+
+ void assertMergedConfig(
+ MergedContextConfiguration mergedConfig,
+ Class> expectedTestClass,
+ String[] expectedLocations,
+ Class>[] expectedClasses,
+ Set>> expectedInitializerClasses,
+ Class extends ContextLoader> expectedContextLoaderClass) {
+ assertNotNull(mergedConfig);
+ assertEquals(expectedTestClass, mergedConfig.getTestClass());
+ assertNotNull(mergedConfig.getLocations());
+ assertArrayEquals(expectedLocations, mergedConfig.getLocations());
+ assertNotNull(mergedConfig.getClasses());
+ assertArrayEquals(expectedClasses, mergedConfig.getClasses());
+ assertNotNull(mergedConfig.getActiveProfiles());
+ assertEquals(expectedContextLoaderClass, mergedConfig.getContextLoader().getClass());
+ assertNotNull(mergedConfig.getContextInitializerClasses());
+ assertEquals(expectedInitializerClasses, mergedConfig.getContextInitializerClasses());
+ }
+
+
+ static class Enigma {
+ }
+
+ @ContextConfiguration
+ @ActiveProfiles
+ static class BareAnnotations {
+ }
+
+ @Configuration
+ static class FooConfig {
+ }
+
+ @Configuration
+ static class BarConfig {
+ }
+
+ @ContextConfiguration(locations = "/foo.xml", inheritLocations = false)
+ @ActiveProfiles(profiles = "foo")
+ static class LocationsFoo {
+ }
+
+ @ContextConfiguration(classes = FooConfig.class, inheritLocations = false)
+ @ActiveProfiles(profiles = "foo")
+ static class ClassesFoo {
+ }
+
+ @ContextConfiguration(locations = "/bar.xml", inheritLocations = true, loader = AnnotationConfigContextLoader.class)
+ @ActiveProfiles("bar")
+ static class LocationsBar extends LocationsFoo {
+ }
+
+ @ContextConfiguration(locations = "/bar.xml", inheritLocations = false, loader = AnnotationConfigContextLoader.class)
+ @ActiveProfiles("bar")
+ static class OverriddenLocationsBar extends LocationsFoo {
+ }
+
+ @ContextConfiguration(classes = BarConfig.class, inheritLocations = true, loader = AnnotationConfigContextLoader.class)
+ @ActiveProfiles("bar")
+ static class ClassesBar extends ClassesFoo {
+ }
+
+ @ContextConfiguration(classes = BarConfig.class, inheritLocations = false, loader = AnnotationConfigContextLoader.class)
+ @ActiveProfiles("bar")
+ static class OverriddenClassesBar extends ClassesFoo {
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
index eb1c39a908..21f15f441f 100644
--- a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
@@ -32,6 +32,7 @@ import static org.springframework.test.context.SpringRunnerContextCacheTests.*;
* conjunction with cache keys used in {@link TestContext}.
*
* @author Sam Brannen
+ * @author Michail Nikolaev
* @since 3.1
* @see SpringRunnerContextCacheTests
*/
@@ -84,6 +85,7 @@ public class ContextCacheTests {
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 3, 1);
loadCtxAndAssertStats(FooBarProfilesTestCase.class, 1, 4, 1);
loadCtxAndAssertStats(BarFooProfilesTestCase.class, 1, 5, 1);
+ loadCtxAndAssertStats(FooBarActiveProfilesResolverTestCase.class, 1, 6, 1);
}
@Test
@@ -287,6 +289,19 @@ public class ContextCacheTests {
private static class BarFooProfilesTestCase {
}
+ private static class FooBarActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "foo", "bar" };
+ }
+ }
+
+ @ActiveProfiles(resolver = FooBarActiveProfilesResolver.class)
+ @ContextConfiguration(classes = Config.class, loader = AnnotationConfigContextLoader.class)
+ private static class FooBarActiveProfilesResolverTestCase {
+ }
+
@ContextHierarchy({ @ContextConfiguration })
private static class ClassHierarchyContextHierarchyLevel1TestCase {
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java
new file mode 100644
index 0000000000..90bfcf0076
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsActiveProfilesTests.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.test.context;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.springframework.test.context.ContextLoaderUtils.*;
+
+/**
+ * Unit tests for {@link ContextLoaderUtils} involving resolution of active bean
+ * definition profiles.
+ *
+ * @author Sam Brannen
+ * @author Michail Nikolaev
+ * @since 3.1
+ */
+public class ContextLoaderUtilsActiveProfilesTests extends AbstractContextLoaderUtilsTests {
+
+ @Test
+ public void resolveActiveProfilesWithoutAnnotation() {
+ String[] profiles = resolveActiveProfiles(Enigma.class);
+ assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
+ }
+
+ @Test
+ public void resolveActiveProfilesWithNoProfilesDeclared() {
+ String[] profiles = resolveActiveProfiles(BareAnnotations.class);
+ assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
+ }
+
+ @Test
+ public void resolveActiveProfilesWithEmptyProfiles() {
+ String[] profiles = resolveActiveProfiles(EmptyProfiles.class);
+ assertArrayEquals(EMPTY_STRING_ARRAY, profiles);
+ }
+
+ @Test
+ public void resolveActiveProfilesWithDuplicatedProfiles() {
+ String[] profiles = resolveActiveProfiles(DuplicatedProfiles.class);
+ assertNotNull(profiles);
+ assertEquals(3, profiles.length);
+
+ List list = Arrays.asList(profiles);
+ assertTrue(list.contains("foo"));
+ assertTrue(list.contains("bar"));
+ assertTrue(list.contains("baz"));
+ }
+
+ @Test
+ public void resolveActiveProfilesWithLocalAnnotation() {
+ String[] profiles = resolveActiveProfiles(LocationsFoo.class);
+ assertNotNull(profiles);
+ assertArrayEquals(new String[] { "foo" }, profiles);
+ }
+
+ @Test
+ public void resolveActiveProfilesWithInheritedAnnotationAndLocations() {
+ String[] profiles = resolveActiveProfiles(InheritedLocationsFoo.class);
+ assertNotNull(profiles);
+ assertArrayEquals(new String[] { "foo" }, profiles);
+ }
+
+ @Test
+ public void resolveActiveProfilesWithInheritedAnnotationAndClasses() {
+ String[] profiles = resolveActiveProfiles(InheritedClassesFoo.class);
+ assertNotNull(profiles);
+ assertArrayEquals(new String[] { "foo" }, profiles);
+ }
+
+ @Test
+ public void resolveActiveProfilesWithLocalAndInheritedAnnotations() {
+ String[] profiles = resolveActiveProfiles(LocationsBar.class);
+ assertNotNull(profiles);
+ assertEquals(2, profiles.length);
+
+ List list = Arrays.asList(profiles);
+ assertTrue(list.contains("foo"));
+ assertTrue(list.contains("bar"));
+ }
+
+ @Test
+ public void resolveActiveProfilesWithOverriddenAnnotation() {
+ String[] profiles = resolveActiveProfiles(Animals.class);
+ assertNotNull(profiles);
+ assertEquals(2, profiles.length);
+
+ List list = Arrays.asList(profiles);
+ assertTrue(list.contains("dog"));
+ assertTrue(list.contains("cat"));
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithResolver() {
+ String[] profiles = resolveActiveProfiles(FooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(1, profiles.length);
+ assertArrayEquals(new String[] { "foo" }, profiles);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithInheritedResolver() {
+ String[] profiles = resolveActiveProfiles(InheritedFooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(1, profiles.length);
+ assertArrayEquals(new String[] { "foo" }, profiles);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithMergedInheritedResolver() {
+ String[] profiles = resolveActiveProfiles(MergedInheritedFooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(2, profiles.length);
+ List list = Arrays.asList(profiles);
+ assertTrue(list.contains("foo"));
+ assertTrue(list.contains("bar"));
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test
+ public void resolveActiveProfilesWithOverridenInheritedResolver() {
+ String[] profiles = resolveActiveProfiles(OverridenInheritedFooActiveProfilesResolverTest.class);
+ assertNotNull(profiles);
+ assertEquals(1, profiles.length);
+ assertArrayEquals(new String[] { "bar" }, profiles);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithConflictingResolverAndProfiles() {
+ resolveActiveProfiles(ConflictingResolverAndProfilesTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithConflictingResolverAndValue() {
+ resolveActiveProfiles(ConflictingResolverAndValueTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithConflictingProfilesAndValue() {
+ resolveActiveProfiles(ConflictingProfilesAndValueTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithResolverWithoutDefaultConstructor() {
+ resolveActiveProfiles(NoDefaultConstructorActiveProfilesResolverTest.class);
+ }
+
+ /**
+ * @since 4.0
+ */
+ @Test(expected = IllegalStateException.class)
+ public void resolveActiveProfilesWithResolverThatReturnsNull() {
+ resolveActiveProfiles(NullActiveProfilesResolverTest.class);
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ @ActiveProfiles({ " ", "\t" })
+ private static class EmptyProfiles {
+ }
+
+ @ActiveProfiles({ "foo", "bar", " foo", "bar ", "baz" })
+ private static class DuplicatedProfiles {
+ }
+
+ @ActiveProfiles(profiles = { "dog", "cat" }, inheritProfiles = false)
+ private static class Animals extends LocationsBar {
+ }
+
+ private static class InheritedLocationsFoo extends LocationsFoo {
+ }
+
+ private static class InheritedClassesFoo extends ClassesFoo {
+ }
+
+ @ActiveProfiles(resolver = NullActiveProfilesResolver.class)
+ private static class NullActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = NoDefaultConstructorActiveProfilesResolver.class)
+ private static class NoDefaultConstructorActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = FooActiveProfilesResolver.class)
+ private static class FooActiveProfilesResolverTest {
+ }
+
+ private static class InheritedFooActiveProfilesResolverTest extends FooActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class)
+ private static class MergedInheritedFooActiveProfilesResolverTest extends InheritedFooActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class, inheritProfiles = false)
+ private static class OverridenInheritedFooActiveProfilesResolverTest extends InheritedFooActiveProfilesResolverTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class, profiles = "conflict")
+ private static class ConflictingResolverAndProfilesTest {
+ }
+
+ @ActiveProfiles(resolver = BarActiveProfilesResolver.class, value = "conflict")
+ private static class ConflictingResolverAndValueTest {
+ }
+
+ @ActiveProfiles(profiles = "conflict", value = "conflict")
+ private static class ConflictingProfilesAndValueTest {
+ }
+
+ private static class FooActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "foo" };
+ }
+ }
+
+ private static class BarActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return new String[] { "bar" };
+ }
+ }
+
+ private static class NullActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return null;
+ }
+ }
+
+ private static class NoDefaultConstructorActiveProfilesResolver implements ActiveProfilesResolver {
+
+ @SuppressWarnings("unused")
+ NoDefaultConstructorActiveProfilesResolver(Object agument) {
+ }
+
+ @Override
+ public String[] resolve(Class> testClass) {
+ return null;
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java
new file mode 100644
index 0000000000..3094581155
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsConfigurationAttributesTests.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.test.context;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+
+import static org.junit.Assert.*;
+import static org.springframework.test.context.ContextLoaderUtils.*;
+
+/**
+ * Unit tests for {@link ContextLoaderUtils} involving {@link ContextConfigurationAttributes}.
+ *
+ * @author Sam Brannen
+ * @since 3.1
+ */
+public class ContextLoaderUtilsConfigurationAttributesTests extends AbstractContextLoaderUtilsTests {
+
+ private void assertAttributes(ContextConfigurationAttributes attributes, Class> expectedDeclaringClass,
+ String[] expectedLocations, Class>[] expectedClasses,
+ Class extends ContextLoader> expectedContextLoaderClass, boolean expectedInheritLocations) {
+ assertEquals(expectedDeclaringClass, attributes.getDeclaringClass());
+ assertArrayEquals(expectedLocations, attributes.getLocations());
+ assertArrayEquals(expectedClasses, attributes.getClasses());
+ assertEquals(expectedInheritLocations, attributes.isInheritLocations());
+ assertEquals(expectedContextLoaderClass, attributes.getContextLoaderClass());
+ }
+
+ private void assertLocationsFooAttributes(ContextConfigurationAttributes attributes) {
+ assertAttributes(attributes, LocationsFoo.class, new String[] { "/foo.xml" }, EMPTY_CLASS_ARRAY,
+ ContextLoader.class, false);
+ }
+
+ private void assertClassesFooAttributes(ContextConfigurationAttributes attributes) {
+ assertAttributes(attributes, ClassesFoo.class, EMPTY_STRING_ARRAY, new Class>[] { FooConfig.class },
+ ContextLoader.class, false);
+ }
+
+ private void assertLocationsBarAttributes(ContextConfigurationAttributes attributes) {
+ assertAttributes(attributes, LocationsBar.class, new String[] { "/bar.xml" }, EMPTY_CLASS_ARRAY,
+ AnnotationConfigContextLoader.class, true);
+ }
+
+ private void assertClassesBarAttributes(ContextConfigurationAttributes attributes) {
+ assertAttributes(attributes, ClassesBar.class, EMPTY_STRING_ARRAY, new Class>[] { BarConfig.class },
+ AnnotationConfigContextLoader.class, true);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void resolveConfigAttributesWithConflictingLocations() {
+ resolveContextConfigurationAttributes(ConflictingLocations.class);
+ }
+
+ @Test
+ public void resolveConfigAttributesWithBareAnnotations() {
+ List attributesList = resolveContextConfigurationAttributes(BareAnnotations.class);
+ assertNotNull(attributesList);
+ assertEquals(1, attributesList.size());
+ assertAttributes(attributesList.get(0), BareAnnotations.class, EMPTY_STRING_ARRAY, EMPTY_CLASS_ARRAY,
+ ContextLoader.class, true);
+ }
+
+ @Test
+ public void resolveConfigAttributesWithLocalAnnotationAndLocations() {
+ List attributesList = resolveContextConfigurationAttributes(LocationsFoo.class);
+ assertNotNull(attributesList);
+ assertEquals(1, attributesList.size());
+ assertLocationsFooAttributes(attributesList.get(0));
+ }
+
+ @Test
+ public void resolveConfigAttributesWithLocalAnnotationAndClasses() {
+ List attributesList = resolveContextConfigurationAttributes(ClassesFoo.class);
+ assertNotNull(attributesList);
+ assertEquals(1, attributesList.size());
+ assertClassesFooAttributes(attributesList.get(0));
+ }
+
+ @Test
+ public void resolveConfigAttributesWithLocalAndInheritedAnnotationsAndLocations() {
+ List attributesList = resolveContextConfigurationAttributes(LocationsBar.class);
+ assertNotNull(attributesList);
+ assertEquals(2, attributesList.size());
+ assertLocationsBarAttributes(attributesList.get(0));
+ assertLocationsFooAttributes(attributesList.get(1));
+ }
+
+ @Test
+ public void resolveConfigAttributesWithLocalAndInheritedAnnotationsAndClasses() {
+ List attributesList = resolveContextConfigurationAttributes(ClassesBar.class);
+ assertNotNull(attributesList);
+ assertEquals(2, attributesList.size());
+ assertClassesBarAttributes(attributesList.get(0));
+ assertClassesFooAttributes(attributesList.get(1));
+ }
+
+
+ @ContextConfiguration(value = "x", locations = "y")
+ private static class ConflictingLocations {
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java
new file mode 100644
index 0000000000..9f59087219
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextHierarchyTests.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.test.context;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.springframework.test.context.ContextLoaderUtils.*;
+
+/**
+ * Unit tests for {@link ContextLoaderUtils} involving context hierarchies.
+ *
+ * @author Sam Brannen
+ * @since 3.1
+ */
+public class ContextLoaderUtilsContextHierarchyTests extends AbstractContextLoaderUtilsTests {
+
+ private void debugConfigAttributes(List configAttributesList) {
+ // for (ContextConfigurationAttributes configAttributes : configAttributesList) {
+ // System.err.println(configAttributes);
+ // }
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void resolveContextHierarchyAttributesForSingleTestClassWithContextConfigurationAndContextHierarchy() {
+ resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchy.class);
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() {
+ List> hierarchyAttributes = resolveContextHierarchyAttributes(BareAnnotations.class);
+ assertEquals(1, hierarchyAttributes.size());
+ List configAttributesList = hierarchyAttributes.get(0);
+ assertEquals(1, configAttributesList.size());
+ debugConfigAttributes(configAttributesList);
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForSingleTestClassWithSingleLevelContextHierarchy() {
+ List> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithSingleLevelContextHierarchy.class);
+ assertEquals(1, hierarchyAttributes.size());
+ List configAttributesList = hierarchyAttributes.get(0);
+ assertEquals(1, configAttributesList.size());
+ debugConfigAttributes(configAttributesList);
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForSingleTestClassWithTripleLevelContextHierarchy() {
+ List> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithTripleLevelContextHierarchy.class);
+ assertEquals(1, hierarchyAttributes.size());
+ List configAttributesList = hierarchyAttributes.get(0);
+ assertEquals(3, configAttributesList.size());
+ debugConfigAttributes(configAttributesList);
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForTestClassHierarchyWithSingleLevelContextHierarchies() {
+ List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass3WithSingleLevelContextHierarchy.class);
+ assertEquals(3, hierarchyAttributes.size());
+
+ List configAttributesListClassLevel1 = hierarchyAttributes.get(0);
+ debugConfigAttributes(configAttributesListClassLevel1);
+ assertEquals(1, configAttributesListClassLevel1.size());
+ assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
+
+ List configAttributesListClassLevel2 = hierarchyAttributes.get(1);
+ debugConfigAttributes(configAttributesListClassLevel2);
+ assertEquals(1, configAttributesListClassLevel2.size());
+ assertArrayEquals(new String[] { "two-A.xml", "two-B.xml" },
+ configAttributesListClassLevel2.get(0).getLocations());
+
+ List configAttributesListClassLevel3 = hierarchyAttributes.get(2);
+ debugConfigAttributes(configAttributesListClassLevel3);
+ assertEquals(1, configAttributesListClassLevel3.size());
+ assertThat(configAttributesListClassLevel3.get(0).getLocations()[0], equalTo("three.xml"));
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() {
+ List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class);
+ assertEquals(2, hierarchyAttributes.size());
+
+ List configAttributesListClassLevel1 = hierarchyAttributes.get(0);
+ debugConfigAttributes(configAttributesListClassLevel1);
+ assertEquals(1, configAttributesListClassLevel1.size());
+ assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
+
+ List configAttributesListClassLevel2 = hierarchyAttributes.get(1);
+ debugConfigAttributes(configAttributesListClassLevel2);
+ assertEquals(1, configAttributesListClassLevel2.size());
+ assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSuperclass() {
+ List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSuperclass.class);
+ assertEquals(2, hierarchyAttributes.size());
+
+ List configAttributesListClassLevel1 = hierarchyAttributes.get(0);
+ debugConfigAttributes(configAttributesListClassLevel1);
+ assertEquals(1, configAttributesListClassLevel1.size());
+ assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
+
+ List configAttributesListClassLevel2 = hierarchyAttributes.get(1);
+ debugConfigAttributes(configAttributesListClassLevel2);
+ assertEquals(1, configAttributesListClassLevel2.size());
+ assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForTestClassHierarchyWithMultiLevelContextHierarchies() {
+ List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass3WithMultiLevelContextHierarchy.class);
+ assertEquals(3, hierarchyAttributes.size());
+
+ List configAttributesListClassLevel1 = hierarchyAttributes.get(0);
+ debugConfigAttributes(configAttributesListClassLevel1);
+ assertEquals(2, configAttributesListClassLevel1.size());
+ assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("1-A.xml"));
+ assertThat(configAttributesListClassLevel1.get(1).getLocations()[0], equalTo("1-B.xml"));
+
+ List configAttributesListClassLevel2 = hierarchyAttributes.get(1);
+ debugConfigAttributes(configAttributesListClassLevel2);
+ assertEquals(2, configAttributesListClassLevel2.size());
+ assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("2-A.xml"));
+ assertThat(configAttributesListClassLevel2.get(1).getLocations()[0], equalTo("2-B.xml"));
+
+ List configAttributesListClassLevel3 = hierarchyAttributes.get(2);
+ debugConfigAttributes(configAttributesListClassLevel3);
+ assertEquals(3, configAttributesListClassLevel3.size());
+ assertThat(configAttributesListClassLevel3.get(0).getLocations()[0], equalTo("3-A.xml"));
+ assertThat(configAttributesListClassLevel3.get(1).getLocations()[0], equalTo("3-B.xml"));
+ assertThat(configAttributesListClassLevel3.get(2).getLocations()[0], equalTo("3-C.xml"));
+ }
+
+ private void assertContextConfigEntriesAreNotUnique(Class> testClass) {
+ try {
+ resolveContextHierarchyAttributes(testClass);
+ fail("Should throw an IllegalStateException");
+ }
+ catch (IllegalStateException e) {
+ String msg = String.format(
+ "The @ContextConfiguration elements configured via @ContextHierarchy in test class [%s] must define unique contexts to load.",
+ testClass.getName());
+ assertEquals(msg, e.getMessage());
+ }
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForSingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig() {
+ assertContextConfigEntriesAreNotUnique(SingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig.class);
+ }
+
+ @Test
+ public void resolveContextHierarchyAttributesForSingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig() {
+ assertContextConfigEntriesAreNotUnique(SingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig.class);
+ }
+
+ @Test
+ public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchies() {
+ Map> map = buildContextHierarchyMap(TestClass3WithMultiLevelContextHierarchy.class);
+
+ assertThat(map.size(), is(3));
+ assertThat(map.keySet(), hasItems("alpha", "beta", "gamma"));
+
+ List alphaConfig = map.get("alpha");
+ assertThat(alphaConfig.size(), is(3));
+ assertThat(alphaConfig.get(0).getLocations()[0], is("1-A.xml"));
+ assertThat(alphaConfig.get(1).getLocations()[0], is("2-A.xml"));
+ assertThat(alphaConfig.get(2).getLocations()[0], is("3-A.xml"));
+
+ List betaConfig = map.get("beta");
+ assertThat(betaConfig.size(), is(3));
+ assertThat(betaConfig.get(0).getLocations()[0], is("1-B.xml"));
+ assertThat(betaConfig.get(1).getLocations()[0], is("2-B.xml"));
+ assertThat(betaConfig.get(2).getLocations()[0], is("3-B.xml"));
+
+ List gammaConfig = map.get("gamma");
+ assertThat(gammaConfig.size(), is(1));
+ assertThat(gammaConfig.get(0).getLocations()[0], is("3-C.xml"));
+ }
+
+ @Test
+ public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchiesAndUnnamedConfig() {
+ Map> map = buildContextHierarchyMap(TestClass3WithMultiLevelContextHierarchyAndUnnamedConfig.class);
+
+ String level1 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 1;
+ String level2 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 2;
+ String level3 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 3;
+ String level4 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 4;
+ String level5 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 5;
+ String level6 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 6;
+ String level7 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 7;
+
+ assertThat(map.size(), is(7));
+ assertThat(map.keySet(), hasItems(level1, level2, level3, level4, level5, level6, level7));
+
+ List level1Config = map.get(level1);
+ assertThat(level1Config.size(), is(1));
+ assertThat(level1Config.get(0).getLocations()[0], is("1-A.xml"));
+
+ List level2Config = map.get(level2);
+ assertThat(level2Config.size(), is(1));
+ assertThat(level2Config.get(0).getLocations()[0], is("1-B.xml"));
+
+ List level3Config = map.get(level3);
+ assertThat(level3Config.size(), is(1));
+ assertThat(level3Config.get(0).getLocations()[0], is("2-A.xml"));
+
+ // ...
+
+ List level7Config = map.get(level7);
+ assertThat(level7Config.size(), is(1));
+ assertThat(level7Config.get(0).getLocations()[0], is("3-C.xml"));
+ }
+
+ @Test
+ public void buildContextHierarchyMapForTestClassHierarchyWithMultiLevelContextHierarchiesAndPartiallyNamedConfig() {
+ Map> map = buildContextHierarchyMap(TestClass2WithMultiLevelContextHierarchyAndPartiallyNamedConfig.class);
+
+ String level1 = "parent";
+ String level2 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 2;
+ String level3 = GENERATED_CONTEXT_HIERARCHY_LEVEL_PREFIX + 3;
+
+ assertThat(map.size(), is(3));
+ assertThat(map.keySet(), hasItems(level1, level2, level3));
+ Iterator levels = map.keySet().iterator();
+ assertThat(levels.next(), is(level1));
+ assertThat(levels.next(), is(level2));
+ assertThat(levels.next(), is(level3));
+
+ List level1Config = map.get(level1);
+ assertThat(level1Config.size(), is(2));
+ assertThat(level1Config.get(0).getLocations()[0], is("1-A.xml"));
+ assertThat(level1Config.get(1).getLocations()[0], is("2-A.xml"));
+
+ List level2Config = map.get(level2);
+ assertThat(level2Config.size(), is(1));
+ assertThat(level2Config.get(0).getLocations()[0], is("1-B.xml"));
+
+ List level3Config = map.get(level3);
+ assertThat(level3Config.size(), is(1));
+ assertThat(level3Config.get(0).getLocations()[0], is("2-C.xml"));
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ @ContextConfiguration("foo.xml")
+ @ContextHierarchy(@ContextConfiguration("bar.xml"))
+ private static class SingleTestClassWithContextConfigurationAndContextHierarchy {
+ }
+
+ @ContextHierarchy(@ContextConfiguration("A.xml"))
+ private static class SingleTestClassWithSingleLevelContextHierarchy {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration("A.xml"),//
+ @ContextConfiguration("B.xml"),//
+ @ContextConfiguration("C.xml") //
+ })
+ private static class SingleTestClassWithTripleLevelContextHierarchy {
+ }
+
+ @ContextHierarchy(@ContextConfiguration("one.xml"))
+ private static class TestClass1WithSingleLevelContextHierarchy {
+ }
+
+ @ContextHierarchy(@ContextConfiguration({ "two-A.xml", "two-B.xml" }))
+ private static class TestClass2WithSingleLevelContextHierarchy extends TestClass1WithSingleLevelContextHierarchy {
+ }
+
+ @ContextHierarchy(@ContextConfiguration("three.xml"))
+ private static class TestClass3WithSingleLevelContextHierarchy extends TestClass2WithSingleLevelContextHierarchy {
+ }
+
+ @ContextConfiguration("one.xml")
+ private static class TestClass1WithBareContextConfigurationInSuperclass {
+ }
+
+ @ContextHierarchy(@ContextConfiguration("two.xml"))
+ private static class TestClass2WithBareContextConfigurationInSuperclass extends
+ TestClass1WithBareContextConfigurationInSuperclass {
+ }
+
+ @ContextHierarchy(@ContextConfiguration("one.xml"))
+ private static class TestClass1WithBareContextConfigurationInSubclass {
+ }
+
+ @ContextConfiguration("two.xml")
+ private static class TestClass2WithBareContextConfigurationInSubclass extends
+ TestClass1WithBareContextConfigurationInSuperclass {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "1-A.xml", name = "alpha"),//
+ @ContextConfiguration(locations = "1-B.xml", name = "beta") //
+ })
+ private static class TestClass1WithMultiLevelContextHierarchy {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "2-A.xml", name = "alpha"),//
+ @ContextConfiguration(locations = "2-B.xml", name = "beta") //
+ })
+ private static class TestClass2WithMultiLevelContextHierarchy extends TestClass1WithMultiLevelContextHierarchy {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "3-A.xml", name = "alpha"),//
+ @ContextConfiguration(locations = "3-B.xml", name = "beta"),//
+ @ContextConfiguration(locations = "3-C.xml", name = "gamma") //
+ })
+ private static class TestClass3WithMultiLevelContextHierarchy extends TestClass2WithMultiLevelContextHierarchy {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "1-A.xml"),//
+ @ContextConfiguration(locations = "1-B.xml") //
+ })
+ private static class TestClass1WithMultiLevelContextHierarchyAndUnnamedConfig {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "2-A.xml"),//
+ @ContextConfiguration(locations = "2-B.xml") //
+ })
+ private static class TestClass2WithMultiLevelContextHierarchyAndUnnamedConfig extends
+ TestClass1WithMultiLevelContextHierarchyAndUnnamedConfig {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "3-A.xml"),//
+ @ContextConfiguration(locations = "3-B.xml"),//
+ @ContextConfiguration(locations = "3-C.xml") //
+ })
+ private static class TestClass3WithMultiLevelContextHierarchyAndUnnamedConfig extends
+ TestClass2WithMultiLevelContextHierarchyAndUnnamedConfig {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "1-A.xml", name = "parent"),//
+ @ContextConfiguration(locations = "1-B.xml") //
+ })
+ private static class TestClass1WithMultiLevelContextHierarchyAndPartiallyNamedConfig {
+ }
+
+ @ContextHierarchy({//
+ //
+ @ContextConfiguration(locations = "2-A.xml", name = "parent"),//
+ @ContextConfiguration(locations = "2-C.xml") //
+ })
+ private static class TestClass2WithMultiLevelContextHierarchyAndPartiallyNamedConfig extends
+ TestClass1WithMultiLevelContextHierarchyAndPartiallyNamedConfig {
+ }
+
+ @ContextHierarchy({
+ //
+ @ContextConfiguration,//
+ @ContextConfiguration //
+ })
+ private static class SingleTestClassWithMultiLevelContextHierarchyWithEmptyContextConfig {
+ }
+
+ @ContextHierarchy({
+ //
+ @ContextConfiguration("foo.xml"),//
+ @ContextConfiguration(classes = BarConfig.class),// duplicate!
+ @ContextConfiguration("baz.xml"),//
+ @ContextConfiguration(classes = BarConfig.class),// duplicate!
+ @ContextConfiguration(loader = AnnotationConfigContextLoader.class) //
+ })
+ private static class SingleTestClassWithMultiLevelContextHierarchyWithDuplicatedContextConfig {
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextInitializerTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextInitializerTests.java
new file mode 100644
index 0000000000..654efb2bd6
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsContextInitializerTests.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.test.context;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Test;
+import org.springframework.context.ApplicationContextInitializer;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.test.context.support.DelegatingSmartContextLoader;
+import org.springframework.web.context.support.GenericWebApplicationContext;
+
+import static org.springframework.test.context.ContextLoaderUtils.*;
+
+/**
+ * Unit tests for {@link ContextLoaderUtils} involving {@link ApplicationContextInitializer}s.
+ *
+ * @author Sam Brannen
+ * @since 3.1
+ */
+public class ContextLoaderUtilsContextInitializerTests extends AbstractContextLoaderUtilsTests {
+
+ @Test
+ public void buildMergedConfigWithLocalInitializer() {
+ Class> testClass = InitializersFoo.class;
+ Class>[] expectedClasses = new Class>[] { FooConfig.class };
+ Set>> expectedInitializerClasses//
+ = new HashSet>>();
+ expectedInitializerClasses.add(FooInitializer.class);
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
+ DelegatingSmartContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithLocalAndInheritedInitializer() {
+ Class> testClass = InitializersBar.class;
+ Class>[] expectedClasses = new Class>[] { FooConfig.class, BarConfig.class };
+ Set>> expectedInitializerClasses//
+ = new HashSet>>();
+ expectedInitializerClasses.add(FooInitializer.class);
+ expectedInitializerClasses.add(BarInitializer.class);
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
+ DelegatingSmartContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithOverriddenInitializers() {
+ Class> testClass = OverriddenInitializersBar.class;
+ Class>[] expectedClasses = new Class>[] { FooConfig.class, BarConfig.class };
+ Set>> expectedInitializerClasses//
+ = new HashSet>>();
+ expectedInitializerClasses.add(BarInitializer.class);
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
+ DelegatingSmartContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithOverriddenInitializersAndClasses() {
+ Class> testClass = OverriddenInitializersAndClassesBar.class;
+ Class>[] expectedClasses = new Class>[] { BarConfig.class };
+ Set>> expectedInitializerClasses//
+ = new HashSet>>();
+ expectedInitializerClasses.add(BarInitializer.class);
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses, expectedInitializerClasses,
+ DelegatingSmartContextLoader.class);
+ }
+
+
+ private static class FooInitializer implements ApplicationContextInitializer {
+
+ @Override
+ public void initialize(GenericApplicationContext applicationContext) {
+ }
+ }
+
+ private static class BarInitializer implements ApplicationContextInitializer {
+
+ @Override
+ public void initialize(GenericWebApplicationContext applicationContext) {
+ }
+ }
+
+ @ContextConfiguration(classes = FooConfig.class, initializers = FooInitializer.class)
+ private static class InitializersFoo {
+ }
+
+ @ContextConfiguration(classes = BarConfig.class, initializers = BarInitializer.class)
+ private static class InitializersBar extends InitializersFoo {
+ }
+
+ @ContextConfiguration(classes = BarConfig.class, initializers = BarInitializer.class, inheritInitializers = false)
+ private static class OverriddenInitializersBar extends InitializersFoo {
+ }
+
+ @ContextConfiguration(classes = BarConfig.class, inheritLocations = false, initializers = BarInitializer.class, inheritInitializers = false)
+ private static class OverriddenInitializersAndClassesBar extends InitializersFoo {
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsMergedConfigTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsMergedConfigTests.java
new file mode 100644
index 0000000000..c2db87bdf0
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsMergedConfigTests.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.test.context;
+
+import org.junit.Test;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+import org.springframework.test.context.support.DelegatingSmartContextLoader;
+import org.springframework.test.context.support.GenericPropertiesContextLoader;
+
+import static org.springframework.test.context.ContextLoaderUtils.*;
+
+/**
+ * Unit tests for {@link ContextLoaderUtils} involving {@link MergedContextConfiguration}.
+ *
+ * @author Sam Brannen
+ * @since 3.1
+ */
+public class ContextLoaderUtilsMergedConfigTests extends AbstractContextLoaderUtilsTests {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void buildMergedConfigWithoutAnnotation() {
+ buildMergedContextConfiguration(Enigma.class, null, null);
+ }
+
+ @Test
+ public void buildMergedConfigWithBareAnnotations() {
+ Class testClass = BareAnnotations.class;
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+
+ assertMergedConfig(
+ mergedConfig,
+ testClass,
+ new String[] { "classpath:/org/springframework/test/context/AbstractContextLoaderUtilsTests$BareAnnotations-context.xml" },
+ EMPTY_CLASS_ARRAY, DelegatingSmartContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithLocalAnnotationAndLocations() {
+ Class> testClass = LocationsFoo.class;
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+
+ assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
+ DelegatingSmartContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithLocalAnnotationAndClasses() {
+ Class> testClass = ClassesFoo.class;
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class>[] { FooConfig.class },
+ DelegatingSmartContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithLocalAnnotationAndOverriddenContextLoaderAndLocations() {
+ Class> testClass = LocationsFoo.class;
+ Class extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
+ expectedContextLoaderClass.getName(), null);
+
+ assertMergedConfig(mergedConfig, testClass, new String[] { "classpath:/foo.xml" }, EMPTY_CLASS_ARRAY,
+ expectedContextLoaderClass);
+ }
+
+ @Test
+ public void buildMergedConfigWithLocalAnnotationAndOverriddenContextLoaderAndClasses() {
+ Class> testClass = ClassesFoo.class;
+ Class extends ContextLoader> expectedContextLoaderClass = GenericPropertiesContextLoader.class;
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass,
+ expectedContextLoaderClass.getName(), null);
+
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, new Class>[] { FooConfig.class },
+ expectedContextLoaderClass);
+ }
+
+ @Test
+ public void buildMergedConfigWithLocalAndInheritedAnnotationsAndLocations() {
+ Class> testClass = LocationsBar.class;
+ String[] expectedLocations = new String[] { "/foo.xml", "/bar.xml" };
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
+ AnnotationConfigContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithLocalAndInheritedAnnotationsAndClasses() {
+ Class> testClass = ClassesBar.class;
+ Class>[] expectedClasses = new Class>[] { FooConfig.class, BarConfig.class };
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
+ AnnotationConfigContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithAnnotationsAndOverriddenLocations() {
+ Class> testClass = OverriddenLocationsBar.class;
+ String[] expectedLocations = new String[] { "/bar.xml" };
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, expectedLocations, EMPTY_CLASS_ARRAY,
+ AnnotationConfigContextLoader.class);
+ }
+
+ @Test
+ public void buildMergedConfigWithAnnotationsAndOverriddenClasses() {
+ Class> testClass = OverriddenClassesBar.class;
+ Class>[] expectedClasses = new Class>[] { BarConfig.class };
+
+ MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass, null, null);
+ assertMergedConfig(mergedConfig, testClass, EMPTY_STRING_ARRAY, expectedClasses,
+ AnnotationConfigContextLoader.class);
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java
deleted file mode 100644
index ecedda4ddf..0000000000
--- a/spring-test/src/test/java/org/springframework/test/context/ContextLoaderUtilsTests.java
+++ /dev/null
@@ -1,846 +0,0 @@
-/*
- * Copyright 2002-2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.test.context;
-
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-import static org.springframework.test.context.ContextLoaderUtils.*;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.junit.Test;
-import org.springframework.context.ApplicationContextInitializer;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.support.GenericApplicationContext;
-import org.springframework.test.context.support.AnnotationConfigContextLoader;
-import org.springframework.test.context.support.DelegatingSmartContextLoader;
-import org.springframework.test.context.support.GenericPropertiesContextLoader;
-import org.springframework.web.context.support.GenericWebApplicationContext;
-
-/**
- * Unit tests for {@link ContextLoaderUtils}.
- *
- * @author Sam Brannen
- * @since 3.1
- */
-public class ContextLoaderUtilsTests {
-
- private static final Class>[] EMPTY_CLASS_ARRAY = new Class>[0];
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final Set>> EMPTY_INITIALIZER_CLASSES = //
- Collections.>> emptySet();
-
-
- private void assertAttributes(ContextConfigurationAttributes attributes, Class> expectedDeclaringClass,
- String[] expectedLocations, Class>[] expectedClasses,
- Class extends ContextLoader> expectedContextLoaderClass, boolean expectedInheritLocations) {
- assertEquals(expectedDeclaringClass, attributes.getDeclaringClass());
- assertArrayEquals(expectedLocations, attributes.getLocations());
- assertArrayEquals(expectedClasses, attributes.getClasses());
- assertEquals(expectedInheritLocations, attributes.isInheritLocations());
- assertEquals(expectedContextLoaderClass, attributes.getContextLoaderClass());
- }
-
- private void assertLocationsFooAttributes(ContextConfigurationAttributes attributes) {
- assertAttributes(attributes, LocationsFoo.class, new String[] { "/foo.xml" }, EMPTY_CLASS_ARRAY,
- ContextLoader.class, false);
- }
-
- private void assertClassesFooAttributes(ContextConfigurationAttributes attributes) {
- assertAttributes(attributes, ClassesFoo.class, EMPTY_STRING_ARRAY, new Class>[] { FooConfig.class },
- ContextLoader.class, false);
- }
-
- private void assertLocationsBarAttributes(ContextConfigurationAttributes attributes) {
- assertAttributes(attributes, LocationsBar.class, new String[] { "/bar.xml" }, EMPTY_CLASS_ARRAY,
- AnnotationConfigContextLoader.class, true);
- }
-
- private void assertClassesBarAttributes(ContextConfigurationAttributes attributes) {
- assertAttributes(attributes, ClassesBar.class, EMPTY_STRING_ARRAY, new Class>[] { BarConfig.class },
- AnnotationConfigContextLoader.class, true);
- }
-
- private void assertMergedConfig(MergedContextConfiguration mergedConfig, Class> expectedTestClass,
- String[] expectedLocations, Class>[] expectedClasses,
- Class extends ContextLoader> expectedContextLoaderClass) {
- assertMergedConfig(mergedConfig, expectedTestClass, expectedLocations, expectedClasses,
- EMPTY_INITIALIZER_CLASSES, expectedContextLoaderClass);
- }
-
- private void assertMergedConfig(
- MergedContextConfiguration mergedConfig,
- Class> expectedTestClass,
- String[] expectedLocations,
- Class>[] expectedClasses,
- Set>> expectedInitializerClasses,
- Class extends ContextLoader> expectedContextLoaderClass) {
- assertNotNull(mergedConfig);
- assertEquals(expectedTestClass, mergedConfig.getTestClass());
- assertNotNull(mergedConfig.getLocations());
- assertArrayEquals(expectedLocations, mergedConfig.getLocations());
- assertNotNull(mergedConfig.getClasses());
- assertArrayEquals(expectedClasses, mergedConfig.getClasses());
- assertNotNull(mergedConfig.getActiveProfiles());
- assertEquals(expectedContextLoaderClass, mergedConfig.getContextLoader().getClass());
- assertNotNull(mergedConfig.getContextInitializerClasses());
- assertEquals(expectedInitializerClasses, mergedConfig.getContextInitializerClasses());
- }
-
- private void debugConfigAttributes(List configAttributesList) {
- // for (ContextConfigurationAttributes configAttributes : configAttributesList) {
- // System.err.println(configAttributes);
- // }
- }
-
- @Test(expected = IllegalStateException.class)
- public void resolveContextHierarchyAttributesForSingleTestClassWithContextConfigurationAndContextHierarchy() {
- resolveContextHierarchyAttributes(SingleTestClassWithContextConfigurationAndContextHierarchy.class);
- }
-
- @Test
- public void resolveContextHierarchyAttributesForSingleTestClassWithImplicitSingleLevelContextHierarchy() {
- List> hierarchyAttributes = resolveContextHierarchyAttributes(BareAnnotations.class);
- assertEquals(1, hierarchyAttributes.size());
- List configAttributesList = hierarchyAttributes.get(0);
- assertEquals(1, configAttributesList.size());
- debugConfigAttributes(configAttributesList);
- }
-
- @Test
- public void resolveContextHierarchyAttributesForSingleTestClassWithSingleLevelContextHierarchy() {
- List> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithSingleLevelContextHierarchy.class);
- assertEquals(1, hierarchyAttributes.size());
- List configAttributesList = hierarchyAttributes.get(0);
- assertEquals(1, configAttributesList.size());
- debugConfigAttributes(configAttributesList);
- }
-
- @Test
- public void resolveContextHierarchyAttributesForSingleTestClassWithTripleLevelContextHierarchy() {
- List> hierarchyAttributes = resolveContextHierarchyAttributes(SingleTestClassWithTripleLevelContextHierarchy.class);
- assertEquals(1, hierarchyAttributes.size());
- List configAttributesList = hierarchyAttributes.get(0);
- assertEquals(3, configAttributesList.size());
- debugConfigAttributes(configAttributesList);
- }
-
- @Test
- public void resolveContextHierarchyAttributesForTestClassHierarchyWithSingleLevelContextHierarchies() {
- List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass3WithSingleLevelContextHierarchy.class);
- assertEquals(3, hierarchyAttributes.size());
-
- List configAttributesListClassLevel1 = hierarchyAttributes.get(0);
- debugConfigAttributes(configAttributesListClassLevel1);
- assertEquals(1, configAttributesListClassLevel1.size());
- assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
-
- List configAttributesListClassLevel2 = hierarchyAttributes.get(1);
- debugConfigAttributes(configAttributesListClassLevel2);
- assertEquals(1, configAttributesListClassLevel2.size());
- assertArrayEquals(new String[] { "two-A.xml", "two-B.xml" },
- configAttributesListClassLevel2.get(0).getLocations());
-
- List configAttributesListClassLevel3 = hierarchyAttributes.get(2);
- debugConfigAttributes(configAttributesListClassLevel3);
- assertEquals(1, configAttributesListClassLevel3.size());
- assertThat(configAttributesListClassLevel3.get(0).getLocations()[0], equalTo("three.xml"));
- }
-
- @Test
- public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSubclass() {
- List> hierarchyAttributes = resolveContextHierarchyAttributes(TestClass2WithBareContextConfigurationInSubclass.class);
- assertEquals(2, hierarchyAttributes.size());
-
- List configAttributesListClassLevel1 = hierarchyAttributes.get(0);
- debugConfigAttributes(configAttributesListClassLevel1);
- assertEquals(1, configAttributesListClassLevel1.size());
- assertThat(configAttributesListClassLevel1.get(0).getLocations()[0], equalTo("one.xml"));
-
- List configAttributesListClassLevel2 = hierarchyAttributes.get(1);
- debugConfigAttributes(configAttributesListClassLevel2);
- assertEquals(1, configAttributesListClassLevel2.size());
- assertThat(configAttributesListClassLevel2.get(0).getLocations()[0], equalTo("two.xml"));
- }
-
- @Test
- public void resolveContextHierarchyAttributesForTestClassHierarchyWithBareContextConfigurationInSuperclass() {
- List