-
Notifications
You must be signed in to change notification settings - Fork 5.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8071693: Introspector ignores default interface methods #13544
Changes from 2 commits
b343ba1
e37a146
0326aa1
1d2ae2c
106edcd
60301d4
1ecc839
ac90a10
8c12996
6b43627
e6a2ecb
823c4f1
b92726a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,16 +24,29 @@ | |
/* | ||
* @test | ||
* @bug 8071693 | ||
* @summary Verify that the Introspector finds default methods inherited from interfaces | ||
* @summary Verify that the Introspector finds default methods inherited | ||
* from interfaces | ||
*/ | ||
|
||
import java.beans.IntrospectionException; | ||
import java.beans.Introspector; | ||
import java.beans.PropertyDescriptor; | ||
import java.util.Arrays; | ||
import java.lang.reflect.Method; | ||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import java.util.NavigableSet; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
public class DefaultMethodBeanPropertyTest { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we please add two additional tests to verify the "diamond" case:
We also can test the case if the D from the cases above is interface and implemented by the class E. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure thing... see 6b43627. |
||
|
||
public interface IfaceA { | ||
////////////////////////////////////// | ||
// // | ||
// SCENARIO 1 // | ||
// // | ||
////////////////////////////////////// | ||
|
||
public interface A1 { | ||
default int getValue() { | ||
return 0; | ||
} | ||
|
@@ -46,14 +59,14 @@ public static int getStaticValue() { | |
} | ||
} | ||
|
||
public interface IfaceB extends IfaceA { | ||
public interface B1 extends A1 { | ||
} | ||
|
||
public interface IfaceC extends IfaceA { | ||
public interface C1 extends A1 { | ||
Number getFoo(); | ||
} | ||
|
||
public class ClassB implements IfaceC { | ||
public class D1 implements C1 { | ||
@Override | ||
public Integer getFoo() { | ||
return null; | ||
|
@@ -64,30 +77,135 @@ public Float getObj() { | |
} | ||
} | ||
|
||
public static void findProperty(Class<?> type, String name) { | ||
PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(type, name); | ||
if (pd == null) { | ||
throw new Error("property \"" + name + "\" not found in " + type); | ||
public static void testScenario1() { | ||
verifyProperties(D1.class, | ||
"getClass", // inherited method | ||
"getValue", // inherited default method | ||
"getFoo", // overridden interface method | ||
"getObj" // overridden default method | ||
); | ||
} | ||
|
||
////////////////////////////////////// | ||
// // | ||
// SCENARIO 2 // | ||
// // | ||
////////////////////////////////////// | ||
|
||
public interface A2 { | ||
default Object getFoo() { | ||
return null; | ||
} | ||
} | ||
|
||
public static void main(String[] args) throws Exception { | ||
findProperty(ClassB.class, "foo"); | ||
public interface B2 extends A2 { | ||
} | ||
|
||
// Expected properties | ||
public interface C2 extends A2 { | ||
} | ||
|
||
public class D2 implements B2, C2 { | ||
} | ||
|
||
public static void testScenario2() { | ||
verifyProperties(D2.class, | ||
"getClass", | ||
"getFoo" | ||
); | ||
} | ||
|
||
////////////////////////////////////// | ||
// // | ||
// SCENARIO 3 // | ||
// // | ||
////////////////////////////////////// | ||
|
||
public interface A3 { | ||
default Object getFoo() { | ||
return null; | ||
} | ||
} | ||
|
||
public interface B3 extends A3 { | ||
@Override | ||
Set<?> getFoo(); | ||
} | ||
|
||
public interface C3 extends A3 { | ||
@Override | ||
Collection<?> getFoo(); | ||
} | ||
|
||
public class D3 implements B3, C3 { | ||
@Override | ||
public NavigableSet<?> getFoo() { | ||
return null; | ||
} | ||
} | ||
|
||
public static void testScenario3() { | ||
verifyProperties(D3.class, | ||
"getClass", | ||
"getFoo" | ||
); | ||
} | ||
|
||
// Helper methods | ||
|
||
public static void verifyProperties(Class<?> type, String... getterNames) { | ||
|
||
// Gather expected properties | ||
final HashSet<PropertyDescriptor> expected = new HashSet<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please split the long lines to use 80 chars per line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in e6a2ecb. |
||
expected.add(new PropertyDescriptor("class", ClassB.class, "getClass", null)); // inherited method | ||
expected.add(new PropertyDescriptor("value", ClassB.class, "getValue", null)); // inherited default method | ||
expected.add(new PropertyDescriptor("foo", ClassB.class, "getFoo", null)); // overridden interface method | ||
expected.add(new PropertyDescriptor("obj", ClassB.class, "getObj", null)); // overridden default method | ||
for (String methodName : getterNames) { | ||
final String suffix = methodName.substring(3); | ||
final String propName = Introspector.decapitalize(suffix); | ||
final Method getter; | ||
try { | ||
getter = type.getMethod(methodName); | ||
} catch (NoSuchMethodException e) { | ||
throw new Error("unexpected error", e); | ||
} | ||
final PropertyDescriptor propDesc; | ||
try { | ||
propDesc = new PropertyDescriptor(propName, getter, null); | ||
} catch (IntrospectionException e) { | ||
throw new Error("unexpected error", e); | ||
} | ||
expected.add(propDesc); | ||
} | ||
|
||
// Verify properties can be found directly | ||
expected.stream() | ||
.map(PropertyDescriptor::getName) | ||
.filter(name -> BeanUtils.getPropertyDescriptor(type, name) == null) | ||
.findFirst() | ||
.ifPresent(name -> { | ||
throw new Error("property \"" + name + "\" not found in " + type); | ||
}); | ||
archiecobbs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Actual properties | ||
final HashSet<PropertyDescriptor> actual = new HashSet<>( | ||
Arrays.asList(BeanUtils.getPropertyDescriptors(ClassB.class))); | ||
// Gather actual properties | ||
final Set<PropertyDescriptor> actual = Set.of( | ||
BeanUtils.getPropertyDescriptors(type)); | ||
archiecobbs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Verify they are the same | ||
// Verify the two sets are the same | ||
if (!actual.equals(expected)) { | ||
throw new Error("mismatch:\n actual: " + actual + "\n expected: " + expected); | ||
throw new Error("mismatch: " + type | ||
+ "\nACTUAL:\n " | ||
+ actual.stream() | ||
.map(Object::toString) | ||
.collect(Collectors.joining("\n ")) | ||
+ "\nEXPECTED:\n " | ||
+ expected.stream() | ||
.map(Object::toString) | ||
.collect(Collectors.joining("\n "))); | ||
archiecobbs marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
// Main method | ||
|
||
public static void main(String[] args) throws Exception { | ||
testScenario1(); | ||
testScenario2(); | ||
testScenario3(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it expected that the test file doesn't compile with simple
javac DefaultMethodBeanPropertyTest.java
? It complains aboutBeanUtils
not found. When run with jtreg, the test compiles successfully.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely not. For example, many regression tests use classes that are not part of the standard JDK such as
toolbox.ToolBox
.