@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/ *
* Copyright 2002 - 2020 the original author or authors .
* Copyright 2002 - 2021 the original author 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,16 @@ package org.springframework.beans;
@@ -19,12 +19,16 @@ package org.springframework.beans;
import java.beans.Introspector ;
import java.beans.PropertyDescriptor ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.InvocationTargetException ;
import java.lang.reflect.Method ;
import java.lang.reflect.Proxy ;
import java.net.URI ;
import java.net.URL ;
import java.time.DayOfWeek ;
import java.time.LocalDateTime ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.Date ;
import java.util.List ;
import java.util.Locale ;
@ -196,6 +200,29 @@ class BeanUtilsTests {
@@ -196,6 +200,29 @@ class BeanUtilsTests {
assertThat ( longListHolder . getList ( ) ) . isEmpty ( ) ;
}
@Test // gh-26531
void copyPropertiesIgnoresGenericsIfSourceOrTargetHasUnresolvableGenerics ( ) throws Exception {
Order original = new Order ( "test" , Arrays . asList ( "foo" , "bar" ) ) ;
// Create a Proxy that loses the generic type information for the getLineItems() method.
OrderSummary proxy = proxyOrder ( original ) ;
assertThat ( OrderSummary . class . getDeclaredMethod ( "getLineItems" ) . toGenericString ( ) )
. contains ( "java.util.List<java.lang.String>" ) ;
assertThat ( proxy . getClass ( ) . getDeclaredMethod ( "getLineItems" ) . toGenericString ( ) )
. contains ( "java.util.List" )
. doesNotContain ( "<java.lang.String>" ) ;
// Ensure that our custom Proxy works as expected.
assertThat ( proxy . getId ( ) ) . isEqualTo ( "test" ) ;
assertThat ( proxy . getLineItems ( ) ) . containsExactly ( "foo" , "bar" ) ;
// Copy from proxy to target.
Order target = new Order ( ) ;
BeanUtils . copyProperties ( proxy , target ) ;
assertThat ( target . getId ( ) ) . isEqualTo ( "test" ) ;
assertThat ( target . getLineItems ( ) ) . containsExactly ( "foo" , "bar" ) ;
}
@Test
void copyPropertiesWithEditable ( ) throws Exception {
TestBean tb = new TestBean ( ) ;
@ -633,4 +660,77 @@ class BeanUtilsTests {
@@ -633,4 +660,77 @@ class BeanUtilsTests {
}
}
@SuppressWarnings ( "unused" )
private static class Order {
private String id ;
private List < String > lineItems ;
Order ( ) {
}
Order ( String id , List < String > lineItems ) {
this . id = id ;
this . lineItems = lineItems ;
}
public String getId ( ) {
return id ;
}
public void setId ( String id ) {
this . id = id ;
}
public List < String > getLineItems ( ) {
return this . lineItems ;
}
public void setLineItems ( List < String > lineItems ) {
this . lineItems = lineItems ;
}
@Override
public String toString ( ) {
return "Order [id=" + this . id + ", lineItems=" + this . lineItems + "]" ;
}
}
private interface OrderSummary {
String getId ( ) ;
List < String > getLineItems ( ) ;
}
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 ;
}
@Override
public Object invoke ( Object proxy , Method method , Object [ ] args ) throws Throwable {
try {
// Ignore args since OrderSummary doesn't declare any methods with arguments,
// and we're not supporting equals(Object), etc.
return Order . class . getDeclaredMethod ( method . getName ( ) ) . invoke ( this . order ) ;
}
catch ( InvocationTargetException ex ) {
throw ex . getTargetException ( ) ;
}
}
}
}