|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2020 the original author or authors. |
| 2 | + * Copyright 2002-2021 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
19 | 19 | import java.beans.Introspector;
|
20 | 20 | import java.beans.PropertyDescriptor;
|
21 | 21 | import java.lang.reflect.Constructor;
|
| 22 | +import java.lang.reflect.InvocationHandler; |
| 23 | +import java.lang.reflect.InvocationTargetException; |
22 | 24 | import java.lang.reflect.Method;
|
| 25 | +import java.lang.reflect.Proxy; |
23 | 26 | import java.net.URI;
|
24 | 27 | import java.net.URL;
|
25 | 28 | import java.time.DayOfWeek;
|
26 | 29 | import java.time.LocalDateTime;
|
27 | 30 | import java.util.ArrayList;
|
| 31 | +import java.util.Arrays; |
28 | 32 | import java.util.Date;
|
29 | 33 | import java.util.List;
|
30 | 34 | import java.util.Locale;
|
@@ -196,6 +200,29 @@ void copyPropertiesDoesNotHonorGenericTypeMismatches() {
|
196 | 200 | assertThat(longListHolder.getList()).isEmpty();
|
197 | 201 | }
|
198 | 202 |
|
| 203 | + @Test // gh-26531 |
| 204 | + void copyPropertiesIgnoresGenericsIfSourceOrTargetHasUnresolvableGenerics() throws Exception { |
| 205 | + Order original = new Order("test", Arrays.asList("foo", "bar")); |
| 206 | + |
| 207 | + // Create a Proxy that loses the generic type information for the getLineItems() method. |
| 208 | + OrderSummary proxy = proxyOrder(original); |
| 209 | + assertThat(OrderSummary.class.getDeclaredMethod("getLineItems").toGenericString()) |
| 210 | + .contains("java.util.List<java.lang.String>"); |
| 211 | + assertThat(proxy.getClass().getDeclaredMethod("getLineItems").toGenericString()) |
| 212 | + .contains("java.util.List") |
| 213 | + .doesNotContain("<java.lang.String>"); |
| 214 | + |
| 215 | + // Ensure that our custom Proxy works as expected. |
| 216 | + assertThat(proxy.getId()).isEqualTo("test"); |
| 217 | + assertThat(proxy.getLineItems()).containsExactly("foo", "bar"); |
| 218 | + |
| 219 | + // Copy from proxy to target. |
| 220 | + Order target = new Order(); |
| 221 | + BeanUtils.copyProperties(proxy, target); |
| 222 | + assertThat(target.getId()).isEqualTo("test"); |
| 223 | + assertThat(target.getLineItems()).containsExactly("foo", "bar"); |
| 224 | + } |
| 225 | + |
199 | 226 | @Test
|
200 | 227 | void copyPropertiesWithEditable() throws Exception {
|
201 | 228 | TestBean tb = new TestBean();
|
@@ -633,4 +660,77 @@ private PrivateBeanWithPrivateConstructor() {
|
633 | 660 | }
|
634 | 661 | }
|
635 | 662 |
|
| 663 | + @SuppressWarnings("unused") |
| 664 | + private static class Order { |
| 665 | + |
| 666 | + private String id; |
| 667 | + private List<String> lineItems; |
| 668 | + |
| 669 | + |
| 670 | + Order() { |
| 671 | + } |
| 672 | + |
| 673 | + Order(String id, List<String> lineItems) { |
| 674 | + this.id = id; |
| 675 | + this.lineItems = lineItems; |
| 676 | + } |
| 677 | + |
| 678 | + public String getId() { |
| 679 | + return id; |
| 680 | + } |
| 681 | + |
| 682 | + public void setId(String id) { |
| 683 | + this.id = id; |
| 684 | + } |
| 685 | + |
| 686 | + public List<String> getLineItems() { |
| 687 | + return this.lineItems; |
| 688 | + } |
| 689 | + |
| 690 | + public void setLineItems(List<String> lineItems) { |
| 691 | + this.lineItems = lineItems; |
| 692 | + } |
| 693 | + |
| 694 | + @Override |
| 695 | + public String toString() { |
| 696 | + return "Order [id=" + this.id + ", lineItems=" + this.lineItems + "]"; |
| 697 | + } |
| 698 | + } |
| 699 | + |
| 700 | + private interface OrderSummary { |
| 701 | + |
| 702 | + String getId(); |
| 703 | + |
| 704 | + List<String> getLineItems(); |
| 705 | + } |
| 706 | + |
| 707 | + |
| 708 | + private OrderSummary proxyOrder(Order order) { |
| 709 | + return (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(), |
| 710 | + new Class<?>[] { OrderSummary.class }, new OrderInvocationHandler(order)); |
| 711 | + } |
| 712 | + |
| 713 | + |
| 714 | + private static class OrderInvocationHandler implements InvocationHandler { |
| 715 | + |
| 716 | + private final Order order; |
| 717 | + |
| 718 | + |
| 719 | + OrderInvocationHandler(Order order) { |
| 720 | + this.order = order; |
| 721 | + } |
| 722 | + |
| 723 | + @Override |
| 724 | + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| 725 | + try { |
| 726 | + // Ignore args since OrderSummary doesn't declare any methods with arguments, |
| 727 | + // and we're not supporting equals(Object), etc. |
| 728 | + return Order.class.getDeclaredMethod(method.getName()).invoke(this.order); |
| 729 | + } |
| 730 | + catch (InvocationTargetException ex) { |
| 731 | + throw ex.getTargetException(); |
| 732 | + } |
| 733 | + } |
| 734 | + } |
| 735 | + |
636 | 736 | }
|
0 commit comments