Compare commits

..
1 Commits
Author SHA1 Message Date
Spring Builds 2399806d5f Release v5.3.36 2024-05-22 15:15:47 +00:00
17 changed files with 132 additions and 738 deletions
+3 -3
View File
@@ -28,8 +28,8 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
mavenBom "io.netty:netty-bom:4.1.111.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.45"
mavenBom "io.netty:netty-bom:4.1.109.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.44"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.3"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.54.v20240208"
@@ -362,7 +362,7 @@ configure([rootProject] + javaProjects) { project ->
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.42")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
}
ext.javadocLinks = [
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.37
version=5.3.36
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
@@ -63,7 +63,7 @@ class EnableCachingIntegrationTests {
// attempt was made to look up the AJ aspect. It's due to classpath issues
// in .integration-tests that it's not found.
assertThatException().isThrownBy(ctx::refresh)
.withMessageContaining("AspectJCachingConfiguration");
.withMessageContaining("AspectJCachingConfiguration");
}
@@ -561,7 +561,8 @@ public class EnvironmentSystemIntegrationTests {
{
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setRequiredProperties("foo", "bar");
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(ctx::refresh);
assertThatExceptionOfType(MissingRequiredPropertiesException.class).isThrownBy(
ctx::refresh);
}
{
@@ -97,7 +97,7 @@ class EnableTransactionManagementIntegrationTests {
ctx.register(Config.class, AspectJTxConfig.class);
// this test is a bit fragile, but gets the job done, proving that an
// attempt was made to look up the AJ aspect. It's due to classpath issues
// in integration-tests that it's not found.
// in .integration-tests that it's not found.
assertThatException()
.isThrownBy(ctx::refresh)
.withMessageContaining("AspectJJtaTransactionManagementConfiguration");
@@ -18,7 +18,6 @@ package org.springframework.aop.aspectj;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
@@ -87,8 +86,6 @@ import org.springframework.util.StringUtils;
public class AspectJExpressionPointcut extends AbstractExpressionPointcut
implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
private static final String AJC_MAGIC = "ajc$";
private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
static {
@@ -110,8 +107,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Nullable
private Class<?> pointcutDeclarationScope;
private boolean aspectCompiledByAjc;
private String[] pointcutParameterNames = new String[0];
private Class<?>[] pointcutParameterTypes = new Class<?>[0];
@@ -143,7 +138,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
* @param paramTypes the parameter types for the pointcut
*/
public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
setPointcutDeclarationScope(declarationScope);
this.pointcutDeclarationScope = declarationScope;
if (paramNames.length != paramTypes.length) {
throw new IllegalStateException(
"Number of pointcut parameter names must match number of pointcut parameter types");
@@ -158,7 +153,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
*/
public void setPointcutDeclarationScope(Class<?> pointcutDeclarationScope) {
this.pointcutDeclarationScope = pointcutDeclarationScope;
this.aspectCompiledByAjc = compiledByAjc(pointcutDeclarationScope);
}
/**
@@ -284,11 +278,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Class<?> targetClass) {
if (this.pointcutParsingFailed) {
// Pointcut parsing failed before below -> avoid trying again.
return false;
}
if (this.aspectCompiledByAjc && compiledByAjc(targetClass)) {
// ajc-compiled aspect class for ajc-compiled target class -> already weaved.
return false;
}
@@ -543,15 +532,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
return shadowMatch;
}
private static boolean compiledByAjc(Class<?> clazz) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith(AJC_MAGIC)) {
return true;
}
}
return false;
}
@Override
public boolean equals(@Nullable Object other) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,12 +22,9 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.reflect.PerClauseKind;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.lang.Nullable;
@@ -43,8 +40,6 @@ import org.springframework.util.Assert;
*/
public class BeanFactoryAspectJAdvisorsBuilder {
private static final Log logger = LogFactory.getLog(BeanFactoryAspectJAdvisorsBuilder.class);
private final ListableBeanFactory beanFactory;
private final AspectJAdvisorFactory advisorFactory;
@@ -107,37 +102,30 @@ public class BeanFactoryAspectJAdvisorsBuilder {
continue;
}
if (this.advisorFactory.isAspect(beanType)) {
try {
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
aspectNames.add(beanName);
advisors.addAll(classAdvisors);
}
catch (IllegalArgumentException | IllegalStateException | AopConfigException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring incompatible aspect [" + beanType.getName() + "]: " + ex);
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
@@ -213,16 +213,8 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
return null;
}
try {
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
catch (IllegalArgumentException | IllegalStateException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring incompatible advice method: " + candidateAdviceMethod, ex);
}
return null;
}
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
@Nullable
@@ -2,29 +2,21 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache-3.1.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">
<aop:aspectj-autoproxy/>
<context:spring-configured/>
<cache:annotation-driven mode="aspectj"/>
<bean id="cacheManager" class="org.springframework.cache.support.NoOpCacheManager"/>
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect" factory-method="aspectOf">
<property name="foo" value="bar"/>
</bean>
<bean id="otherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
<bean id="otherBean" class="java.lang.Object"/>
<bean id="yetAnotherBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring"/>
<bean id="configuredBean" class="org.springframework.beans.factory.aspectj.ShouldBeConfiguredBySpring" lazy-init="true"/>
<bean id="yetAnotherBean" class="java.lang.Object"/>
</beans>
@@ -24,7 +24,8 @@
</property>
</bean>
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<bean id="defaultCache"
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="default"/>
</bean>
@@ -7,10 +7,12 @@
http://www.springframework.org/schema/task
https://www.springframework.org/schema/task/spring-task.xsd">
<task:annotation-driven mode="aspectj" executor="testExecutor" exception-handler="testExceptionHandler"/>
<task:annotation-driven mode="aspectj" executor="testExecutor"
exception-handler="testExceptionHandler"/>
<task:executor id="testExecutor"/>
<bean id="testExceptionHandler" class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
<bean id="testExceptionHandler"
class="org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler"/>
</beans>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,8 +42,6 @@ import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.testfixture.beans.DerivedTestBean;
import org.springframework.beans.testfixture.beans.ITestBean;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.lang.Nullable;
@@ -54,7 +52,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import static org.assertj.core.api.SoftAssertions.assertSoftly;
/**
* Tests for {@link BeanUtils}.
* Unit tests for {@link BeanUtils}.
*
* @author Juergen Hoeller
* @author Rob Harrop
@@ -138,7 +136,7 @@ class BeanUtilsTests {
PropertyDescriptor[] actual = Introspector.getBeanInfo(TestBean.class).getPropertyDescriptors();
PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(TestBean.class);
assertThat(descriptors).as("Descriptors should not be null").isNotNull();
assertThat(descriptors).as("Invalid number of descriptors returned").hasSameSizeAs(actual);
assertThat(descriptors.length).as("Invalid number of descriptors returned").isEqualTo(actual.length);
}
@Test
@@ -164,13 +162,13 @@ class BeanUtilsTests {
tb.setAge(32);
tb.setTouchy("touchy");
TestBean tb2 = new TestBean();
assertThat(tb2.getName()).as("Name empty").isNull();
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
assertThat(tb2.getName() == null).as("Name empty").isTrue();
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
BeanUtils.copyProperties(tb, tb2);
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
}
@Test
@@ -180,13 +178,13 @@ class BeanUtilsTests {
tb.setAge(32);
tb.setTouchy("touchy");
TestBean tb2 = new TestBean();
assertThat(tb2.getName()).as("Name empty").isNull();
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
assertThat(tb2.getName() == null).as("Name empty").isTrue();
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
BeanUtils.copyProperties(tb, tb2);
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
}
@Test
@@ -196,13 +194,13 @@ class BeanUtilsTests {
tb.setAge(32);
tb.setTouchy("touchy");
DerivedTestBean tb2 = new DerivedTestBean();
assertThat(tb2.getName()).as("Name empty").isNull();
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
assertThat(tb2.getName() == null).as("Name empty").isTrue();
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
BeanUtils.copyProperties(tb, tb2);
assertThat(tb2.getName()).as("Name copied").isEqualTo(tb.getName());
assertThat(tb2.getAge()).as("Age copied").isEqualTo(tb.getAge());
assertThat(tb2.getTouchy()).as("Touchy copied").isEqualTo(tb.getTouchy());
assertThat(tb2.getName().equals(tb.getName())).as("Name copied").isTrue();
assertThat(tb2.getAge() == tb.getAge()).as("Age copied").isTrue();
assertThat(tb2.getTouchy().equals(tb.getTouchy())).as("Touchy copied").isTrue();
}
/**
@@ -229,8 +227,8 @@ class BeanUtilsTests {
IntegerListHolder2 integerListHolder2 = new IntegerListHolder2();
BeanUtils.copyProperties(integerListHolder1, integerListHolder2);
assertThat(integerListHolder1.getList()).containsExactly(42);
assertThat(integerListHolder2.getList()).containsExactly(42);
assertThat(integerListHolder1.getList()).containsOnly(42);
assertThat(integerListHolder2.getList()).containsOnly(42);
}
/**
@@ -259,7 +257,7 @@ class BeanUtilsTests {
WildcardListHolder2 wildcardListHolder2 = new WildcardListHolder2();
BeanUtils.copyProperties(integerListHolder1, wildcardListHolder2);
assertThat(integerListHolder1.getList()).containsExactly(42);
assertThat(integerListHolder1.getList()).containsOnly(42);
assertThat(wildcardListHolder2.getList()).isEqualTo(Arrays.asList(42));
}
@@ -273,8 +271,9 @@ class BeanUtilsTests {
NumberUpperBoundedWildcardListHolder numberListHolder = new NumberUpperBoundedWildcardListHolder();
BeanUtils.copyProperties(integerListHolder1, numberListHolder);
assertThat(integerListHolder1.getList()).containsExactly(42);
assertThat(numberListHolder.getList()).isEqualTo(Arrays.asList(42));
assertThat(integerListHolder1.getList()).containsOnly(42);
assertThat(numberListHolder.getList()).hasSize(1);
assertThat(numberListHolder.getList().contains(Integer.valueOf(42))).isTrue();
}
/**
@@ -283,7 +282,7 @@ class BeanUtilsTests {
@Test
void copyPropertiesDoesNotCopyFromSuperTypeToSubType() {
NumberHolder numberHolder = new NumberHolder();
numberHolder.setNumber(42);
numberHolder.setNumber(Integer.valueOf(42));
IntegerHolder integerHolder = new IntegerHolder();
BeanUtils.copyProperties(numberHolder, integerHolder);
@@ -301,7 +300,7 @@ class BeanUtilsTests {
LongListHolder longListHolder = new LongListHolder();
BeanUtils.copyProperties(integerListHolder, longListHolder);
assertThat(integerListHolder.getList()).containsExactly(42);
assertThat(integerListHolder.getList()).containsOnly(42);
assertThat(longListHolder.getList()).isEmpty();
}
@@ -315,7 +314,7 @@ class BeanUtilsTests {
NumberListHolder numberListHolder = new NumberListHolder();
BeanUtils.copyProperties(integerListHolder, numberListHolder);
assertThat(integerListHolder.getList()).containsExactly(42);
assertThat(integerListHolder.getList()).containsOnly(42);
assertThat(numberListHolder.getList()).isEmpty();
}
@@ -324,13 +323,12 @@ class BeanUtilsTests {
Order original = new Order("test", Arrays.asList("foo", "bar"));
// Create a Proxy that loses the generic type information for the getLineItems() method.
OrderSummary proxy = (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class<?>[] {OrderSummary.class}, new OrderInvocationHandler(original));
OrderSummary proxy = proxyOrder(original);
assertThat(OrderSummary.class.getDeclaredMethod("getLineItems").toGenericString())
.contains("java.util.List<java.lang.String>");
.contains("java.util.List<java.lang.String>");
assertThat(proxy.getClass().getDeclaredMethod("getLineItems").toGenericString())
.contains("java.util.List")
.doesNotContain("<java.lang.String>");
.contains("java.util.List")
.doesNotContain("<java.lang.String>");
// Ensure that our custom Proxy works as expected.
assertThat(proxy.getId()).isEqualTo("test");
@@ -343,57 +341,40 @@ class BeanUtilsTests {
assertThat(target.getLineItems()).containsExactly("foo", "bar");
}
@Test // gh-32888
public void copyPropertiesWithGenericCglibClass() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(User.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> proxy.invokeSuper(obj, args));
User user = (User) enhancer.create();
user.setId(1);
user.setName("proxy");
user.setAddress("addr");
User target = new User();
BeanUtils.copyProperties(user, target);
assertThat(target.getId()).isEqualTo(user.getId());
assertThat(target.getName()).isEqualTo(user.getName());
assertThat(target.getAddress()).isEqualTo(user.getAddress());
}
@Test
void copyPropertiesWithEditable() throws Exception {
TestBean tb = new TestBean();
assertThat(tb.getName()).as("Name empty").isNull();
assertThat(tb.getName() == null).as("Name empty").isTrue();
tb.setAge(32);
tb.setTouchy("bla");
TestBean tb2 = new TestBean();
tb2.setName("rod");
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
// "touchy" should not be copied: it's not defined in ITestBean
BeanUtils.copyProperties(tb, tb2, ITestBean.class);
assertThat(tb2.getName()).as("Name copied").isNull();
assertThat(tb2.getAge()).as("Age copied").isEqualTo(32);
assertThat(tb2.getTouchy()).as("Touchy still empty").isNull();
assertThat(tb2.getName() == null).as("Name copied").isTrue();
assertThat(tb2.getAge() == 32).as("Age copied").isTrue();
assertThat(tb2.getTouchy() == null).as("Touchy still empty").isTrue();
}
@Test
void copyPropertiesWithIgnore() throws Exception {
TestBean tb = new TestBean();
assertThat(tb.getName()).as("Name empty").isNull();
assertThat(tb.getName() == null).as("Name empty").isTrue();
tb.setAge(32);
tb.setTouchy("bla");
TestBean tb2 = new TestBean();
tb2.setName("rod");
assertThat(tb2.getAge()).as("Age empty").isEqualTo(0);
assertThat(tb2.getTouchy()).as("Touchy empty").isNull();
assertThat(tb2.getAge() == 0).as("Age empty").isTrue();
assertThat(tb2.getTouchy() == null).as("Touchy empty").isTrue();
// "spouse", "touchy", "age" should not be copied
BeanUtils.copyProperties(tb, tb2, "spouse", "touchy", "age");
assertThat(tb2.getName()).as("Name copied").isNull();
assertThat(tb2.getAge()).as("Age still empty").isEqualTo(0);
assertThat(tb2.getTouchy()).as("Touchy still empty").isNull();
assertThat(tb2.getName() == null).as("Name copied").isTrue();
assertThat(tb2.getAge() == 0).as("Age still empty").isTrue();
assertThat(tb2.getTouchy() == null).as("Touchy still empty").isTrue();
}
@Test
@@ -402,7 +383,7 @@ class BeanUtilsTests {
source.setName("name");
TestBean target = new TestBean();
BeanUtils.copyProperties(source, target, "specialProperty");
assertThat(target.getName()).isEqualTo("name");
assertThat("name").isEqualTo(target.getName());
}
@Test
@@ -539,7 +520,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class IntegerHolder {
@@ -554,7 +534,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class WildcardListHolder1 {
@@ -569,7 +548,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class WildcardListHolder2 {
@@ -584,7 +562,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class NumberUpperBoundedWildcardListHolder {
@@ -599,7 +576,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class NumberListHolder {
@@ -614,7 +590,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class IntegerListHolder1 {
@@ -629,7 +604,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class IntegerListHolder2 {
@@ -644,7 +618,6 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class LongListHolder {
@@ -825,7 +798,6 @@ class BeanUtilsTests {
}
}
private static class BeanWithNullableTypes {
private Integer counter;
@@ -856,7 +828,6 @@ class BeanUtilsTests {
}
}
private static class BeanWithPrimitiveTypes {
private boolean flag;
@@ -869,6 +840,7 @@ class BeanUtilsTests {
private char character;
private String text;
@SuppressWarnings("unused")
public BeanWithPrimitiveTypes(boolean flag, byte byteCount, short shortCount, int intCount, long longCount,
float floatCount, double doubleCount, char character, String text) {
@@ -919,8 +891,8 @@ class BeanUtilsTests {
public String getText() {
return text;
}
}
}
private static class PrivateBeanWithPrivateConstructor {
@@ -928,14 +900,13 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class Order {
private String id;
private List<String> lineItems;
Order() {
}
@@ -966,7 +937,6 @@ class BeanUtilsTests {
}
}
private interface OrderSummary {
String getId();
@@ -975,10 +945,17 @@ class BeanUtilsTests {
}
private OrderSummary proxyOrder(Order order) {
return (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class<?>[] { OrderSummary.class }, new OrderInvocationHandler(order));
}
private static class OrderInvocationHandler implements InvocationHandler {
private final Order order;
OrderInvocationHandler(Order order) {
this.order = order;
}
@@ -996,46 +973,4 @@ class BeanUtilsTests {
}
}
private static class GenericBaseModel<T> {
private T id;
private String name;
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private static class User extends GenericBaseModel<Integer> {
private String address;
public User() {
super();
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,11 +45,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Spring's default implementation of the {@link LifecycleProcessor} strategy.
*
* <p>Provides interaction with {@link Lifecycle} and {@link SmartLifecycle} beans in
* groups for specific phases, on startup/shutdown as well as for explicit start/stop
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
* Default implementation of the {@link LifecycleProcessor} strategy.
*
* @author Mark Fisher
* @author Juergen Hoeller
@@ -149,13 +145,13 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int startupPhase = getPhase(bean);
phases.computeIfAbsent(startupPhase,
phase -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
int phase = getPhase(bean);
phases.computeIfAbsent(
phase,
p -> new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly)
).add(beanName, bean);
}
});
if (!phases.isEmpty()) {
phases.values().forEach(LifecycleGroup::start);
}
@@ -195,7 +191,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
private void stopBeans() {
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
int shutdownPhase = getPhase(bean);
LifecycleGroup group = phases.get(shutdownPhase);
@@ -205,7 +200,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
}
group.add(beanName, bean);
});
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
keys.sort(Collections.reverseOrder());
@@ -320,8 +314,6 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
/**
* Helper class for maintaining a group of Lifecycle beans that should be started
* and stopped together based on their 'phase' value (or the default value of 0).
* The group is expected to be created in an ad-hoc fashion and group members are
* expected to always have the same 'phase' value.
*/
private class LifecycleGroup {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,11 +57,11 @@ public final class BridgeMethodResolver {
/**
* Find the local original method for the supplied {@link Method bridge Method}.
* Find the original method for the supplied {@link Method bridge Method}.
* <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
* In such a case, the supplied {@link Method} instance is returned directly to the caller.
* Callers are <strong>not</strong> required to check for bridging before calling this method.
* @param bridgeMethod the method to introspect against its declaring class
* @param bridgeMethod the method to introspect
* @return the original method (either the bridged method or the passed-in method
* if no more specific one could be found)
*/
@@ -73,7 +73,8 @@ public final class BridgeMethodResolver {
if (bridgedMethod == null) {
// Gather all methods with matching name and parameter size.
List<Method> candidateMethods = new ArrayList<>();
MethodFilter filter = (candidateMethod -> isBridgedCandidateFor(candidateMethod, bridgeMethod));
MethodFilter filter = candidateMethod ->
isBridgedCandidateFor(candidateMethod, bridgeMethod);
ReflectionUtils.doWithMethods(bridgeMethod.getDeclaringClass(), candidateMethods::add, filter);
if (!candidateMethods.isEmpty()) {
bridgedMethod = candidateMethods.size() == 1 ?
@@ -94,10 +95,10 @@ public final class BridgeMethodResolver {
* Returns {@code true} if the supplied '{@code candidateMethod}' can be
* considered a valid candidate for the {@link Method} that is {@link Method#isBridge() bridged}
* by the supplied {@link Method bridge Method}. This method performs inexpensive
* checks and can be used to quickly filter for a set of possible matches.
* checks and can be used quickly to filter for a set of possible matches.
*/
private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
return (!candidateMethod.isBridge() &&
return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) &&
candidateMethod.getName().equals(bridgeMethod.getName()) &&
candidateMethod.getParameterCount() == bridgeMethod.getParameterCount());
}
@@ -120,8 +121,8 @@ public final class BridgeMethodResolver {
return candidateMethod;
}
else if (previousMethod != null) {
sameSig = sameSig && Arrays.equals(
candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
sameSig = sameSig &&
Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
}
previousMethod = candidateMethod;
}
@@ -162,8 +163,7 @@ public final class BridgeMethodResolver {
}
}
// A non-array type: compare the type itself.
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(
ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
if (!ClassUtils.resolvePrimitiveIfNecessary(candidateParameter).equals(ClassUtils.resolvePrimitiveIfNecessary(genericParameter.toClass()))) {
return false;
}
}
@@ -226,8 +226,8 @@ public final class BridgeMethodResolver {
/**
* Compare the signatures of the bridge method and the method which it bridges. If
* the parameter and return types are the same, it is a 'visibility' bridge method
* introduced in Java 6 to fix <a href="https://bugs.openjdk.org/browse/JDK-6342411">
* JDK-6342411</a>.
* introduced in Java 6 to fix https://bugs.openjdk.org/browse/JDK-6342411.
* See also https://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
* @return whether signatures match as described
*/
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
@@ -1052,16 +1052,16 @@ public abstract class AnnotationUtils {
return null;
}
try {
for (Method method : annotation.annotationType().getDeclaredMethods()) {
if (method.getName().equals(attributeName) && method.getParameterCount() == 0) {
return invokeAnnotationMethod(method, annotation);
}
}
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
return invokeAnnotationMethod(method, annotation);
}
catch (NoSuchMethodException ex) {
return null;
}
catch (Throwable ex) {
handleValueRetrievalFailure(annotation, ex);
return null;
}
return null;
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -226,8 +226,6 @@ public class Indexer extends SpelNodeImpl {
cf.loadTarget(mv);
}
SpelNodeImpl index = this.children[0];
if (this.indexedType == IndexedType.ARRAY) {
int insn;
if ("D".equals(this.exitTypeDescriptor)) {
@@ -264,14 +262,18 @@ public class Indexer extends SpelNodeImpl {
//depthPlusOne(exitTypeDescriptor)+"Ljava/lang/Object;");
insn = AALOAD;
}
generateIndexCode(mv, cf, index, int.class);
SpelNodeImpl index = this.children[0];
cf.enterCompilationScope();
index.generateCode(mv, cf);
cf.exitCompilationScope();
mv.visitInsn(insn);
}
else if (this.indexedType == IndexedType.LIST) {
mv.visitTypeInsn(CHECKCAST, "java/util/List");
generateIndexCode(mv, cf, index, int.class);
cf.enterCompilationScope();
this.children[0].generateCode(mv, cf);
cf.exitCompilationScope();
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "get", "(I)Ljava/lang/Object;", true);
}
@@ -279,13 +281,15 @@ public class Indexer extends SpelNodeImpl {
mv.visitTypeInsn(CHECKCAST, "java/util/Map");
// Special case when the key is an unquoted string literal that will be parsed as
// a property/field reference
if (index instanceof PropertyOrFieldReference) {
if ((this.children[0] instanceof PropertyOrFieldReference)) {
PropertyOrFieldReference reference = (PropertyOrFieldReference) this.children[0];
String mapKeyName = reference.getName();
mv.visitLdcInsn(mapKeyName);
}
else {
generateIndexCode(mv, cf, index, Object.class);
cf.enterCompilationScope();
this.children[0].generateCode(mv, cf);
cf.exitCompilationScope();
}
mv.visitMethodInsn(
INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
@@ -321,11 +325,6 @@ public class Indexer extends SpelNodeImpl {
cf.pushDescriptor(this.exitTypeDescriptor);
}
private void generateIndexCode(MethodVisitor mv, CodeFlow cf, SpelNodeImpl indexNode, Class<?> indexType) {
String indexDesc = CodeFlow.toDescriptor(indexType);
generateCodeForArgument(mv, cf, indexNode, indexDesc);
}
@Override
public String toStringAST() {
StringJoiner sj = new StringJoiner(",", "[", "]");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,8 +20,6 @@ import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -29,10 +27,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.asm.MethodVisitor;
@@ -60,7 +55,6 @@ import static org.assertj.core.api.InstanceOfAssertFactories.BOOLEAN;
* Checks SpelCompiler behavior. This should cover compilation all compiled node types.
*
* @author Andy Clement
* @author Sam Brannen
* @since 4.1
*/
public class SpelCompilationCoverageTests extends AbstractExpressionTests {
@@ -135,488 +129,6 @@ public class SpelCompilationCoverageTests extends AbstractExpressionTests {
private SpelNodeImpl ast;
@Nested
class VariableReferenceTests {
@Test
void userDefinedVariable() {
EvaluationContext ctx = new StandardEvaluationContext();
ctx.setVariable("target", "abc");
expression = parser.parseExpression("#target");
assertThat(expression.getValue(ctx)).isEqualTo("abc");
assertCanCompile(expression);
assertThat(expression.getValue(ctx)).isEqualTo("abc");
ctx.setVariable("target", "123");
assertThat(expression.getValue(ctx)).isEqualTo("123");
// Changing the variable type from String to Integer results in a
// ClassCastException in the compiled code.
ctx.setVariable("target", 42);
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> expression.getValue(ctx))
.withCauseInstanceOf(ClassCastException.class);
ctx.setVariable("target", "abc");
expression = parser.parseExpression("#target.charAt(0)");
assertThat(expression.getValue(ctx)).isEqualTo('a');
assertCanCompile(expression);
assertThat(expression.getValue(ctx)).isEqualTo('a');
ctx.setVariable("target", "1");
assertThat(expression.getValue(ctx)).isEqualTo('1');
// Changing the variable type from String to Integer results in a
// ClassCastException in the compiled code.
ctx.setVariable("target", 42);
assertThatExceptionOfType(SpelEvaluationException.class)
.isThrownBy(() -> expression.getValue(ctx))
.withCauseInstanceOf(ClassCastException.class);
}
}
@Nested
class IndexingTests {
@Test
void indexIntoPrimitiveShortArray() {
short[] shorts = { (short) 33, (short) 44, (short) 55 };
expression = parser.parseExpression("[2]");
assertThat(expression.getValue(shorts)).isEqualTo((short) 55);
assertCanCompile(expression);
assertThat(expression.getValue(shorts)).isEqualTo((short) 55);
assertThat(getAst().getExitDescriptor()).isEqualTo("S");
}
@Test
void indexIntoPrimitiveByteArray() {
byte[] bytes = { (byte) 2, (byte) 3, (byte) 4 };
expression = parser.parseExpression("[2]");
assertThat(expression.getValue(bytes)).isEqualTo((byte) 4);
assertCanCompile(expression);
assertThat(expression.getValue(bytes)).isEqualTo((byte) 4);
assertThat(getAst().getExitDescriptor()).isEqualTo("B");
}
@Test
void indexIntoPrimitiveIntArray() {
int[] ints = { 8, 9, 10 };
expression = parser.parseExpression("[2]");
assertThat(expression.getValue(ints)).isEqualTo(10);
assertCanCompile(expression);
assertThat(expression.getValue(ints)).isEqualTo(10);
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
}
@Test
void indexIntoPrimitiveLongArray() {
long[] longs = { 2L, 3L, 4L };
expression = parser.parseExpression("[0]");
assertThat(expression.getValue(longs)).isEqualTo(2L);
assertCanCompile(expression);
assertThat(expression.getValue(longs)).isEqualTo(2L);
assertThat(getAst().getExitDescriptor()).isEqualTo("J");
}
@Test
void indexIntoPrimitiveFloatArray() {
float[] floats = { 6.0f, 7.0f, 8.0f };
expression = parser.parseExpression("[0]");
assertThat(expression.getValue(floats)).isEqualTo(6.0f);
assertCanCompile(expression);
assertThat(expression.getValue(floats)).isEqualTo(6.0f);
assertThat(getAst().getExitDescriptor()).isEqualTo("F");
}
@Test
void indexIntoPrimitiveDoubleArray() {
double[] doubles = { 3.0d, 4.0d, 5.0d };
expression = parser.parseExpression("[1]");
assertThat(expression.getValue(doubles)).isEqualTo(4.0d);
assertCanCompile(expression);
assertThat(expression.getValue(doubles)).isEqualTo(4.0d);
assertThat(getAst().getExitDescriptor()).isEqualTo("D");
}
@Test
void indexIntoPrimitiveCharArray() {
char[] chars = { 'a', 'b', 'c' };
expression = parser.parseExpression("[1]");
assertThat(expression.getValue(chars)).isEqualTo('b');
assertCanCompile(expression);
assertThat(expression.getValue(chars)).isEqualTo('b');
assertThat(getAst().getExitDescriptor()).isEqualTo("C");
}
@Test
void indexIntoStringArray() {
String[] strings = { "a", "b", "c" };
expression = parser.parseExpression("[0]");
assertThat(expression.getValue(strings)).isEqualTo("a");
assertCanCompile(expression);
assertThat(expression.getValue(strings)).isEqualTo("a");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
}
@Test
void indexIntoNumberArray() {
Number[] numbers = { 2, 8, 9 };
expression = parser.parseExpression("[1]");
assertThat(expression.getValue(numbers)).isEqualTo(8);
assertCanCompile(expression);
assertThat(expression.getValue(numbers)).isEqualTo(8);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Number");
}
@Test
void indexInto2DPrimitiveIntArray() {
int[][] array = new int[][] {
{ 1, 2, 3 },
{ 4, 5, 6 }
};
expression = parser.parseExpression("[1]");
assertThat(stringify(expression.getValue(array))).isEqualTo("4 5 6");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(array))).isEqualTo("4 5 6");
assertThat(getAst().getExitDescriptor()).isEqualTo("[I");
expression = parser.parseExpression("[1][2]");
assertThat(stringify(expression.getValue(array))).isEqualTo("6");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(array))).isEqualTo("6");
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
}
@Test
void indexInto2DStringArray() {
String[][] array = new String[][] {
{ "a", "b", "c" },
{ "d", "e", "f" }
};
expression = parser.parseExpression("[1]");
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
assertCanCompile(expression);
assertThat(getAst().getExitDescriptor()).isEqualTo("[Ljava/lang/String");
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
assertThat(getAst().getExitDescriptor()).isEqualTo("[Ljava/lang/String");
expression = parser.parseExpression("[1][2]");
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
}
@Test
@SuppressWarnings("unchecked")
void indexIntoArrayOfListOfString() {
List<String>[] array = new List[] {
Arrays.asList("a", "b", "c"),
Arrays.asList("d", "e", "f")
};
expression = parser.parseExpression("[1]");
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(array))).isEqualTo("d e f");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/List");
expression = parser.parseExpression("[1][2]");
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(array))).isEqualTo("f");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test
@SuppressWarnings("unchecked")
void indexIntoArrayOfMap() {
Map<String, String>[] array = new Map[] { Collections.singletonMap("key", "value1") };
expression = parser.parseExpression("[0]");
assertThat(stringify(expression.getValue(array))).isEqualTo("{key=value1}");
assertCanCompile(expression);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/Map");
assertThat(stringify(expression.getValue(array))).isEqualTo("{key=value1}");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/util/Map");
expression = parser.parseExpression("[0]['key']");
assertThat(stringify(expression.getValue(array))).isEqualTo("value1");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(array))).isEqualTo("value1");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test
void indexIntoListOfString() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
expression = parser.parseExpression("[1]");
assertThat(expression.getValue(list)).isEqualTo("bbb");
assertCanCompile(expression);
assertThat(expression.getValue(list)).isEqualTo("bbb");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test
void indexIntoListOfInteger() {
List<Integer> list = Arrays.asList(123, 456, 789);
expression = parser.parseExpression("[2]");
assertThat(expression.getValue(list)).isEqualTo(789);
assertCanCompile(expression);
assertThat(expression.getValue(list)).isEqualTo(789);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test
void indexIntoListOfStringArray() {
List<String[]> list = Arrays.asList(
new String[] { "a", "b", "c" },
new String[] { "d", "e", "f" }
);
expression = parser.parseExpression("[1]");
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
expression = parser.parseExpression("[1][0]");
assertThat(stringify(expression.getValue(list))).isEqualTo("d");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(list))).isEqualTo("d");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
}
@Test
void indexIntoListOfIntegerArray() {
List<Integer[]> list = Arrays.asList(
new Integer[] { 1, 2, 3 },
new Integer[] { 4, 5, 6 }
);
expression = parser.parseExpression("[0]");
assertThat(stringify(expression.getValue(list))).isEqualTo("1 2 3");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(list))).isEqualTo("1 2 3");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
expression = parser.parseExpression("[0][1]");
assertThat(expression.getValue(list)).isEqualTo(2);
assertCanCompile(expression);
assertThat(expression.getValue(list)).isEqualTo(2);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Integer");
}
@Test
void indexIntoListOfListOfString() {
List<List<String>> list = Arrays.asList(
Arrays.asList("a", "b", "c"),
Arrays.asList("d", "e", "f")
);
expression = parser.parseExpression("[1]");
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
assertCanCompile(expression);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
assertThat(stringify(expression.getValue(list))).isEqualTo("d e f");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
expression = parser.parseExpression("[1][2]");
assertThat(stringify(expression.getValue(list))).isEqualTo("f");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(list))).isEqualTo("f");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test
void indexIntoMap() {
Map<String, Integer> map = Collections.singletonMap("aaa", 111);
expression = parser.parseExpression("['aaa']");
assertThat(expression.getValue(map)).isEqualTo(111);
assertCanCompile(expression);
assertThat(expression.getValue(map)).isEqualTo(111);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test
void indexIntoMapOfListOfString() {
Map<String, List<String>> map = Collections.singletonMap("foo", Arrays.asList("a", "b", "c"));
expression = parser.parseExpression("['foo']");
assertThat(stringify(expression.getValue(map))).isEqualTo("a b c");
assertCanCompile(expression);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
assertThat(stringify(expression.getValue(map))).isEqualTo("a b c");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
expression = parser.parseExpression("['foo'][2]");
assertThat(stringify(expression.getValue(map))).isEqualTo("c");
assertCanCompile(expression);
assertThat(stringify(expression.getValue(map))).isEqualTo("c");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test
void indexIntoObject() {
TestClass6 tc = new TestClass6();
// field access
expression = parser.parseExpression("['orange']");
assertThat(expression.getValue(tc)).isEqualTo("value1");
assertCanCompile(expression);
assertThat(expression.getValue(tc)).isEqualTo("value1");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
// field access
expression = parser.parseExpression("['peach']");
assertThat(expression.getValue(tc)).isEqualTo(34L);
assertCanCompile(expression);
assertThat(expression.getValue(tc)).isEqualTo(34L);
assertThat(getAst().getExitDescriptor()).isEqualTo("J");
// property access (getter)
expression = parser.parseExpression("['banana']");
assertThat(expression.getValue(tc)).isEqualTo("value3");
assertCanCompile(expression);
assertThat(expression.getValue(tc)).isEqualTo("value3");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/String");
}
@Test // gh-32694, gh-32908
void indexIntoArrayUsingIntegerWrapper() {
context.setVariable("array", new int[] {1, 2, 3, 4});
context.setVariable("index", 2);
expression = parser.parseExpression("#array[#index]");
assertThat(expression.getValue(context)).isEqualTo(3);
assertCanCompile(expression);
assertThat(expression.getValue(context)).isEqualTo(3);
assertThat(getAst().getExitDescriptor()).isEqualTo("I");
}
@Test // gh-32694, gh-32908
void indexIntoListUsingIntegerWrapper() {
context.setVariable("list", Arrays.asList(1, 2, 3, 4));
context.setVariable("index", 2);
expression = parser.parseExpression("#list[#index]");
assertThat(expression.getValue(context)).isEqualTo(3);
assertCanCompile(expression);
assertThat(expression.getValue(context)).isEqualTo(3);
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
@Test // gh-32903
void indexIntoMapUsingPrimitiveLiteral() {
Map<Object, String> map = new HashMap<>();
map.put(false, "0"); // BooleanLiteral
map.put(1, "ABC"); // IntLiteral
map.put(2L, "XYZ"); // LongLiteral
map.put(9.99F, "~10"); // FloatLiteral
map.put(3.14159, "PI"); // RealLiteral
context.setVariable("map", map);
// BooleanLiteral
expression = parser.parseExpression("#map[false]");
assertThat(expression.getValue(context)).isEqualTo("0");
assertCanCompile(expression);
assertThat(expression.getValue(context)).isEqualTo("0");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
// IntLiteral
expression = parser.parseExpression("#map[1]");
assertThat(expression.getValue(context)).isEqualTo("ABC");
assertCanCompile(expression);
assertThat(expression.getValue(context)).isEqualTo("ABC");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
// LongLiteral
expression = parser.parseExpression("#map[2L]");
assertThat(expression.getValue(context)).isEqualTo("XYZ");
assertCanCompile(expression);
assertThat(expression.getValue(context)).isEqualTo("XYZ");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
// FloatLiteral
expression = parser.parseExpression("#map[9.99F]");
assertThat(expression.getValue(context)).isEqualTo("~10");
assertCanCompile(expression);
assertThat(expression.getValue(context)).isEqualTo("~10");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
// RealLiteral
expression = parser.parseExpression("#map[3.14159]");
assertThat(expression.getValue(context)).isEqualTo("PI");
assertCanCompile(expression);
assertThat(expression.getValue(context)).isEqualTo("PI");
assertThat(getAst().getExitDescriptor()).isEqualTo("Ljava/lang/Object");
}
private String stringify(Object object) {
Stream<? extends Object> stream;
if (object instanceof Collection) {
stream = ((Collection<?>) object).stream();
}
else if (object instanceof Object[]) {
stream = Arrays.stream((Object[]) object);
}
else if (object instanceof int[]) {
stream = Arrays.stream((int[]) object).mapToObj(Integer::valueOf);
}
else {
return String.valueOf(object);
}
return stream.map(Object::toString).collect(Collectors.joining(" "));
}
}
@Test
void typeReference() {
expression = parse("T(String)");