From 74241a7cd79f2dda723f07948c7925f9b6511989 Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Sun, 26 Mar 2017 01:49:46 +0430 Subject: [PATCH 1/6] LANG-1317: Add findAnnotation and findMethodsWithAnnotation to MethodUtils --- .../commons/lang3/reflect/MethodUtils.java | 110 ++++++++++++++++++ .../lang3/reflect/MethodUtilsTest.java | 17 +++ .../commons/lang3/reflect/testbed/Foo.java | 1 + .../commons/lang3/reflect/testbed/Parent.java | 4 + .../lang3/reflect/testbed/PublicChild.java | 8 ++ 5 files changed, 140 insertions(+) diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java index 46023782f70..48daa87b394 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java @@ -871,4 +871,114 @@ public static List getMethodsListWithAnnotation(final Class cls, fina return annotatedMethods; } + /** + *

Finds all methods including non public methods of the given class and it's super classes and interfaces + * that are annotated with the given annotation.

+ * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link java.lang.annotation.Annotation} that must be present on a method to be matched + * @return an array of Methods (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.6 + */ + public static Method[] findMethodsWithAnnotation(final Class cls, final Class annotationCls) { + final List annotatedMethodsList = findMethodsListWithAnnotation(cls, annotationCls); + return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]); + } + + /** + *

Finds all methods including non public methods of the given class and it's super classes and interfaces + * that are annotated with the given annotation.

+ * @param cls + * the {@link Class} to query + * @param annotationCls + * the {@link Annotation} that must be present on a method to be matched + * @return a list of Methods (possibly empty). + * @throws IllegalArgumentException + * if the class or annotation are {@code null} + * @since 3.6 + */ + public static List findMethodsListWithAnnotation(final Class cls, final Class annotationCls) { + Validate.isTrue(cls != null, "The class must not be null"); + Validate.isTrue(annotationCls != null, "The annotation class must not be null"); + List> allSuperclasses = ClassUtils.getAllSuperclasses(cls); + allSuperclasses.add(0, cls); + int sci = 0; + List> allInterfaces = ClassUtils.getAllInterfaces(cls); + int ifi = 0; + final List annotatedMethods = new ArrayList<>(); + while (ifi < allInterfaces.size() || + sci < allSuperclasses.size()) { + Class acls; + if (ifi >= allInterfaces.size()) + acls = allSuperclasses.get(sci++); + else if (sci >= allSuperclasses.size()) + acls = allInterfaces.get(ifi++); + else if (sci <= ifi) + acls = allSuperclasses.get(sci++); + else + acls = allInterfaces.get(ifi++); + final Method[] allMethods = acls.getDeclaredMethods(); + for (final Method method : allMethods) { + if (method.getAnnotation(annotationCls) != null) { + annotatedMethods.add(method); + } + } + } + return annotatedMethods; + } + + /** + *

BFS to find the annotation object that is present on the given method or any equivalent method in + * super classes and interfaces, with the given annotation type. Returns null if the annotation type was not present + * on any of them.

+ * @param method + * the {@link Method} to query + * @param annotationCls + * the {@link Annotation} to check if is present on the method + * @return an Annotation (possibly null). + * @throws IllegalArgumentException + * if the method or annotation are {@code null} + * @since 3.6 + */ + public static A findAnnotation(final Method method, final Class annotationCls) { + Validate.isTrue(method != null, "The method must not be null"); + Validate.isTrue(annotationCls != null, "The annotation class must not be null"); + A annotation = method.getAnnotation(annotationCls); + + if(annotation == null) { + Class mcls = method.getDeclaringClass(); + List> allSuperclasses = ClassUtils.getAllSuperclasses(mcls); + int sci = 0; + List> allInterfaces = ClassUtils.getAllInterfaces(mcls); + int ifi = 0; + while (ifi < allInterfaces.size() || + sci < allSuperclasses.size()) { + Class acls; + if(ifi >= allInterfaces.size()) + acls = allSuperclasses.get(sci++); + else if(sci >= allSuperclasses.size()) + acls = allInterfaces.get(ifi++); + else if(ifi <= sci) + acls = allInterfaces.get(ifi++); + else + acls = allSuperclasses.get(sci++); + Method equivalentMethod = null; + try { + equivalentMethod = acls.getDeclaredMethod(method.getName(), method.getParameterTypes()); + } catch (NoSuchMethodException e) { + // If not found, just keep on breadth first search + } + if(equivalentMethod != null) { + annotation = equivalentMethod.getAnnotation(annotationCls); + if(annotation != null) + break; + } + } + } + + return annotation; + } } diff --git a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java index df57a13fd38..76a77dea800 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java @@ -47,6 +47,7 @@ import org.apache.commons.lang3.reflect.testbed.Annotated; import org.apache.commons.lang3.reflect.testbed.GenericConsumer; import org.apache.commons.lang3.reflect.testbed.GenericParent; +import org.apache.commons.lang3.reflect.testbed.PublicChild; import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild; import org.apache.commons.lang3.tuple.ImmutablePair; import org.junit.Assert; @@ -681,6 +682,22 @@ public void testGetMethodsWithAnnotation() throws NoSuchMethodException { assertThat(methodsWithAnnotation, hasItemInArray(MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation"))); } + @Test + public void testFindAnnotationOnMethod() throws NoSuchMethodException { + assertNull(MethodUtils.findAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class)); + assertNotNull(MethodUtils.findAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class)); + assertNotNull(MethodUtils.findAnnotation(PublicChild.class.getDeclaredMethod("parentProtectedAnnotatedMethod"), Annotated.class)); + assertNotNull(MethodUtils.findAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"), Annotated.class)); + } + + @Test + public void testFindMethodsWithAnnotation() throws NoSuchMethodException { + assertArrayEquals(new Method[0], MethodUtils.findMethodsWithAnnotation(Object.class, Annotated.class)); + + final Method[] methodsWithAnnotation = MethodUtils.findMethodsWithAnnotation(PublicChild.class, Annotated.class); + assertEquals(3, methodsWithAnnotation.length); + } + @Test(expected = IllegalArgumentException.class) public void testGetMethodsWithAnnotationIllegalArgumentException1() { MethodUtils.getMethodsWithAnnotation(FieldUtilsTest.class, null); diff --git a/src/test/java/org/apache/commons/lang3/reflect/testbed/Foo.java b/src/test/java/org/apache/commons/lang3/reflect/testbed/Foo.java index 551199360ec..be24ac6acd0 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/testbed/Foo.java +++ b/src/test/java/org/apache/commons/lang3/reflect/testbed/Foo.java @@ -21,5 +21,6 @@ public interface Foo { public static final String VALUE = "foo"; + @Annotated void doIt(); } diff --git a/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java b/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java index 70447b444c7..007ea65292d 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java +++ b/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java @@ -28,4 +28,8 @@ class Parent implements Foo { @Override public void doIt() { } + + @Annotated + protected void parentProtectedAnnotatedMethod() { + } } diff --git a/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java b/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java index c91a06f5878..0fc0239e6cb 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java +++ b/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java @@ -20,4 +20,12 @@ */ public class PublicChild extends Parent { static final String VALUE = "child"; + + @Override + protected void parentProtectedAnnotatedMethod() { + } + + @Annotated + private void privateAnnotatedMethod() { + } } From e49a3a2099759f5fee2423aff2b9fa762881c36b Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Sun, 26 Mar 2017 12:35:22 +0430 Subject: [PATCH 2/6] LANG-1317: A few style corrections --- .../commons/lang3/reflect/MethodUtils.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java index 48daa87b394..57481bf5987 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java @@ -912,14 +912,18 @@ public static List findMethodsListWithAnnotation(final Class cls, fin while (ifi < allInterfaces.size() || sci < allSuperclasses.size()) { Class acls; - if (ifi >= allInterfaces.size()) + if (ifi >= allInterfaces.size()) { acls = allSuperclasses.get(sci++); - else if (sci >= allSuperclasses.size()) + } + else if (sci >= allSuperclasses.size()) { acls = allInterfaces.get(ifi++); - else if (sci <= ifi) + } + else if (sci <= ifi) { acls = allSuperclasses.get(sci++); - else + } + else { acls = allInterfaces.get(ifi++); + } final Method[] allMethods = acls.getDeclaredMethods(); for (final Method method : allMethods) { if (method.getAnnotation(annotationCls) != null) { @@ -934,6 +938,8 @@ else if (sci <= ifi) *

BFS to find the annotation object that is present on the given method or any equivalent method in * super classes and interfaces, with the given annotation type. Returns null if the annotation type was not present * on any of them.

+ * @param
+ * the annotation type * @param method * the {@link Method} to query * @param annotationCls @@ -957,14 +963,18 @@ public static A findAnnotation(final Method method, final while (ifi < allInterfaces.size() || sci < allSuperclasses.size()) { Class acls; - if(ifi >= allInterfaces.size()) + if(ifi >= allInterfaces.size()) { acls = allSuperclasses.get(sci++); - else if(sci >= allSuperclasses.size()) + } + else if(sci >= allSuperclasses.size()) { acls = allInterfaces.get(ifi++); - else if(ifi <= sci) + } + else if(ifi <= sci) { acls = allInterfaces.get(ifi++); - else + } + else { acls = allSuperclasses.get(sci++); + } Method equivalentMethod = null; try { equivalentMethod = acls.getDeclaredMethod(method.getName(), method.getParameterTypes()); @@ -973,8 +983,9 @@ else if(ifi <= sci) } if(equivalentMethod != null) { annotation = equivalentMethod.getAnnotation(annotationCls); - if(annotation != null) + if(annotation != null) { break; + } } } } From 7390433b9dc9f53d2cf472421fe63d8240756492 Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Tue, 28 Mar 2017 15:31:22 +0430 Subject: [PATCH 3/6] LANG-1317: More parametric and readable, refactored duplicate code and more unit tests --- .../org/apache/commons/lang3/ClassUtils.java | 47 ++++++ .../commons/lang3/reflect/MethodUtils.java | 133 ++++++++--------- .../apache/commons/lang3/ClassUtilsTest.java | 32 ++++ .../lang3/reflect/MethodUtilsTest.java | 137 ++++++++++++++++-- .../lang3/reflect/testbed/PublicChild.java | 6 +- 5 files changed, 269 insertions(+), 86 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java index 012d2a6e1a6..34550a67e3a 100644 --- a/src/main/java/org/apache/commons/lang3/ClassUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java @@ -52,6 +52,14 @@ public enum Interfaces { INCLUDE, EXCLUDE } + /** + * Inclusivity literals for {@link #getAllSuperclassesAndInterfaces(Class, Priority)}. + * @since 3.6 + */ + public enum Priority { + SUPERCLASS, INTERFACE + } + /** * The package separator character: '.' == {@value}. */ @@ -456,6 +464,45 @@ public static List> getAllInterfaces(final Class cls) { return new ArrayList<>(interfacesFound); } + /** + *

Gets a combination of {@link #getAllSuperclasses}(Class)} and + * {@link #getAllInterfaces}(Class)}, one from superclasses, one + * from interfaces, and so on in a breadth first way.

+ * + * @param cls the class to look up, may be {@code null} + * @param p determines what to peek in same breadth, the superclass or interface + * @return the {@code List} of superclasses in order going up from this one + * {@code null} if null input + */ + public static List> getAllSuperclassesAndInterfaces(final Class cls, Priority p) { + if (cls == null) { + return null; + } + + final List> classes = new ArrayList<>(); + List> allSuperclasses = ClassUtils.getAllSuperclasses(cls); + int sci = 0; + List> allInterfaces = ClassUtils.getAllInterfaces(cls); + int ifi = 0; + while (ifi < allInterfaces.size() || + sci < allSuperclasses.size()) { + Class acls; + if (ifi >= allInterfaces.size()) { + acls = allSuperclasses.get(sci++); + } else if (sci >= allSuperclasses.size()) { + acls = allInterfaces.get(ifi++); + } else if (ifi < sci) { + acls = allInterfaces.get(ifi++); + } else if (sci < ifi) { + acls = allSuperclasses.get(sci++); + } else { + acls = (p == Priority.SUPERCLASS ? allSuperclasses.get(sci++) : allInterfaces.get(ifi++)); + } + classes.add(acls); + } + return classes; + } + /** * Get the interfaces for the specified class. * diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java index 57481bf5987..0d54d99ff78 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java @@ -832,7 +832,7 @@ public static Set getOverrideHierarchy(final Method method, final Interf } /** - * Gets all methods of the given class that are annotated with the given annotation. + * Gets all class level public methods of the given class that are annotated with the given annotation. * @param cls * the {@link Class} to query * @param annotationCls @@ -843,12 +843,11 @@ public static Set getOverrideHierarchy(final Method method, final Interf * @since 3.4 */ public static Method[] getMethodsWithAnnotation(final Class cls, final Class annotationCls) { - final List annotatedMethodsList = getMethodsListWithAnnotation(cls, annotationCls); - return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]); + return getMethodsWithAnnotation(cls, annotationCls, false, false); } /** - * Gets all methods of the given class that are annotated with the given annotation. + * Gets all class level public methods of the given class that are annotated with the given annotation. * @param cls * the {@link Class} to query * @param annotationCls @@ -859,73 +858,59 @@ public static Method[] getMethodsWithAnnotation(final Class cls, final Class< * @since 3.4 */ public static List getMethodsListWithAnnotation(final Class cls, final Class annotationCls) { - Validate.isTrue(cls != null, "The class must not be null"); - Validate.isTrue(annotationCls != null, "The annotation class must not be null"); - final Method[] allMethods = cls.getMethods(); - final List annotatedMethods = new ArrayList<>(); - for (final Method method : allMethods) { - if (method.getAnnotation(annotationCls) != null) { - annotatedMethods.add(method); - } - } - return annotatedMethods; + return getMethodsListWithAnnotation(cls, annotationCls, false, false); } /** - *

Finds all methods including non public methods of the given class and it's super classes and interfaces - * that are annotated with the given annotation.

+ * Gets all methods of the given class that are annotated with the given annotation. * @param cls * the {@link Class} to query * @param annotationCls * the {@link java.lang.annotation.Annotation} that must be present on a method to be matched + * @param searchSupers + * determines if also a lookup in the entire inheritance hierarchy of the given class should be performed + * @param ignoreAccess + * determines if also non public methods should be considered * @return an array of Methods (possibly empty). * @throws IllegalArgumentException * if the class or annotation are {@code null} * @since 3.6 */ - public static Method[] findMethodsWithAnnotation(final Class cls, final Class annotationCls) { - final List annotatedMethodsList = findMethodsListWithAnnotation(cls, annotationCls); + public static Method[] getMethodsWithAnnotation(final Class cls, final Class annotationCls, + boolean searchSupers, boolean ignoreAccess) { + final List annotatedMethodsList = getMethodsListWithAnnotation(cls, annotationCls, searchSupers, + ignoreAccess); return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]); } /** - *

Finds all methods including non public methods of the given class and it's super classes and interfaces - * that are annotated with the given annotation.

+ * Gets all methods of the given class that are annotated with the given annotation. * @param cls * the {@link Class} to query * @param annotationCls * the {@link Annotation} that must be present on a method to be matched + * @param searchSupers + * determines if also a lookup in the entire inheritance hierarchy of the given class should be performed + * @param ignoreAccess + * determines if also non public methods should be considered * @return a list of Methods (possibly empty). * @throws IllegalArgumentException * if the class or annotation are {@code null} * @since 3.6 */ - public static List findMethodsListWithAnnotation(final Class cls, final Class annotationCls) { + public static List getMethodsListWithAnnotation(final Class cls, + final Class annotationCls, + boolean searchSupers, boolean ignoreAccess) { + Validate.isTrue(cls != null, "The class must not be null"); Validate.isTrue(annotationCls != null, "The annotation class must not be null"); - List> allSuperclasses = ClassUtils.getAllSuperclasses(cls); - allSuperclasses.add(0, cls); - int sci = 0; - List> allInterfaces = ClassUtils.getAllInterfaces(cls); - int ifi = 0; + List> classes = (searchSupers ? ClassUtils.getAllSuperclassesAndInterfaces(cls, + ClassUtils.Priority.INTERFACE) : new ArrayList>()); + classes.add(0, cls); final List annotatedMethods = new ArrayList<>(); - while (ifi < allInterfaces.size() || - sci < allSuperclasses.size()) { - Class acls; - if (ifi >= allInterfaces.size()) { - acls = allSuperclasses.get(sci++); - } - else if (sci >= allSuperclasses.size()) { - acls = allInterfaces.get(ifi++); - } - else if (sci <= ifi) { - acls = allSuperclasses.get(sci++); - } - else { - acls = allInterfaces.get(ifi++); - } - final Method[] allMethods = acls.getDeclaredMethods(); - for (final Method method : allMethods) { + for (Class acls : classes) { + final Method[] methods = (ignoreAccess ? acls.getDeclaredMethods() : acls.getMethods()); + for (final Method method : methods) { if (method.getAnnotation(annotationCls) != null) { annotatedMethods.add(method); } @@ -935,57 +920,53 @@ else if (sci <= ifi) { } /** - *

BFS to find the annotation object that is present on the given method or any equivalent method in - * super classes and interfaces, with the given annotation type. Returns null if the annotation type was not present - * on any of them.

+ *

Gets the annotation object that is present on the given method or any equivalent method in + * super classes and interfaces, with the given annotation type. Returns null if the annotation + * type was not present on any of them.

+ * + *

Stops searching for an annotation once the first annotation of the specified type has been + * found. i.e, additional annotations of the specified type will be silently ignored.

* @param
* the annotation type * @param method * the {@link Method} to query * @param annotationCls * the {@link Annotation} to check if is present on the method - * @return an Annotation (possibly null). + * @param searchSupers + * determines if lookup in the entire inheritance hierarchy of the given class if was not directly present + * @param ignoreAccess + * determines if underlying method has to be accessible + * @return the first matching annotation, or {@code null} if not found * @throws IllegalArgumentException * if the method or annotation are {@code null} * @since 3.6 */ - public static A findAnnotation(final Method method, final Class annotationCls) { + public static A getAnnotation(final Method method, final Class annotationCls, + boolean searchSupers, boolean ignoreAccess) { + Validate.isTrue(method != null, "The method must not be null"); Validate.isTrue(annotationCls != null, "The annotation class must not be null"); + if(!ignoreAccess && !MemberUtils.isAccessible(method)) { + return null; + } + A annotation = method.getAnnotation(annotationCls); - if(annotation == null) { + if(annotation == null && searchSupers) { Class mcls = method.getDeclaringClass(); - List> allSuperclasses = ClassUtils.getAllSuperclasses(mcls); - int sci = 0; - List> allInterfaces = ClassUtils.getAllInterfaces(mcls); - int ifi = 0; - while (ifi < allInterfaces.size() || - sci < allSuperclasses.size()) { - Class acls; - if(ifi >= allInterfaces.size()) { - acls = allSuperclasses.get(sci++); - } - else if(sci >= allSuperclasses.size()) { - acls = allInterfaces.get(ifi++); - } - else if(ifi <= sci) { - acls = allInterfaces.get(ifi++); - } - else { - acls = allSuperclasses.get(sci++); - } - Method equivalentMethod = null; + List> classes = ClassUtils.getAllSuperclassesAndInterfaces(mcls, ClassUtils.Priority.INTERFACE); + for (Class acls : classes) { + Method equivalentMethod; try { - equivalentMethod = acls.getDeclaredMethod(method.getName(), method.getParameterTypes()); + equivalentMethod = (ignoreAccess ? acls.getDeclaredMethod(method.getName(), method.getParameterTypes()) + : acls.getMethod(method.getName(), method.getParameterTypes())); } catch (NoSuchMethodException e) { - // If not found, just keep on breadth first search + // If not found, just keep on search + continue; } - if(equivalentMethod != null) { - annotation = equivalentMethod.getAnnotation(annotationCls); - if(annotation != null) { - break; - } + annotation = equivalentMethod.getAnnotation(annotationCls); + if (annotation != null) { + break; } } } diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java index 744aaa8377a..a956638ce88 100644 --- a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java @@ -281,6 +281,38 @@ public void test_getAllInterfaces_Class() { assertEquals(null, ClassUtils.getAllInterfaces(null)); } + @Test + public void test_getAllSuperclassesAndInterfacesPriorityINTERFACE_Class() { + final List list = ClassUtils.getAllSuperclassesAndInterfaces(CY.class, ClassUtils.Priority.INTERFACE); + assertEquals(8, list.size()); + assertEquals(IB.class, list.get(0)); + assertEquals(CX.class, list.get(1)); + assertEquals(IC.class, list.get(2)); + assertEquals(Object.class, list.get(3)); + assertEquals(ID.class, list.get(4)); + assertEquals(IE.class, list.get(5)); + assertEquals(IF.class, list.get(6)); + assertEquals(IA.class, list.get(7)); + + assertEquals(null, ClassUtils.getAllSuperclassesAndInterfaces(null, ClassUtils.Priority.INTERFACE)); + } + + @Test + public void test_getAllSuperclassesAndInterfacesPrioritySUPERCLASS_Class() { + final List list = ClassUtils.getAllSuperclassesAndInterfaces(CY.class, ClassUtils.Priority.SUPERCLASS); + assertEquals(8, list.size()); + assertEquals(CX.class, list.get(0)); + assertEquals(IB.class, list.get(1)); + assertEquals(Object.class, list.get(2)); + assertEquals(IC.class, list.get(3)); + assertEquals(ID.class, list.get(4)); + assertEquals(IE.class, list.get(5)); + assertEquals(IF.class, list.get(6)); + assertEquals(IA.class, list.get(7)); + + assertEquals(null, ClassUtils.getAllSuperclassesAndInterfaces(null, ClassUtils.Priority.SUPERCLASS)); + } + private static interface IA { } private static interface IB { diff --git a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java index 76a77dea800..7c8489f687a 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java @@ -683,19 +683,122 @@ public void testGetMethodsWithAnnotation() throws NoSuchMethodException { } @Test - public void testFindAnnotationOnMethod() throws NoSuchMethodException { - assertNull(MethodUtils.findAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class)); - assertNotNull(MethodUtils.findAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class)); - assertNotNull(MethodUtils.findAnnotation(PublicChild.class.getDeclaredMethod("parentProtectedAnnotatedMethod"), Annotated.class)); - assertNotNull(MethodUtils.findAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"), Annotated.class)); + public void testGetMethodsWithAnnotationSearchSupersAndIgnoreAccess() throws NoSuchMethodException { + assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class, + true, true)); + + final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class, + true, true); + assertEquals(4, methodsWithAnnotation.length); + assertEquals("PublicChild", methodsWithAnnotation[0].getDeclaringClass().getSimpleName()); + assertEquals("PublicChild", methodsWithAnnotation[1].getDeclaringClass().getSimpleName()); + assertTrue(methodsWithAnnotation[0].getName().endsWith("AnnotatedMethod")); + assertTrue(methodsWithAnnotation[1].getName().endsWith("AnnotatedMethod")); + assertEquals("Foo.doIt", + methodsWithAnnotation[2].getDeclaringClass().getSimpleName() + '.' + + methodsWithAnnotation[2].getName()); + assertEquals("Parent.parentProtectedAnnotatedMethod", + methodsWithAnnotation[3].getDeclaringClass().getSimpleName() + '.' + + methodsWithAnnotation[3].getName()); } @Test - public void testFindMethodsWithAnnotation() throws NoSuchMethodException { - assertArrayEquals(new Method[0], MethodUtils.findMethodsWithAnnotation(Object.class, Annotated.class)); + public void testGetMethodsWithAnnotationNotSearchSupersButIgnoreAccess() throws NoSuchMethodException { + assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class, + false, true)); - final Method[] methodsWithAnnotation = MethodUtils.findMethodsWithAnnotation(PublicChild.class, Annotated.class); - assertEquals(3, methodsWithAnnotation.length); + final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class, + false, true); + assertEquals(2, methodsWithAnnotation.length); + assertEquals("PublicChild", methodsWithAnnotation[0].getDeclaringClass().getSimpleName()); + assertEquals("PublicChild", methodsWithAnnotation[1].getDeclaringClass().getSimpleName()); + assertTrue(methodsWithAnnotation[0].getName().endsWith("AnnotatedMethod")); + assertTrue(methodsWithAnnotation[1].getName().endsWith("AnnotatedMethod")); + } + + @Test + public void testGetMethodsWithAnnotationSearchSupersButNotIgnoreAccess() throws NoSuchMethodException { + assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class, + true, false)); + + final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class, + true, false); + assertEquals(2, methodsWithAnnotation.length); + assertEquals("PublicChild.publicAnnotatedMethod", + methodsWithAnnotation[0].getDeclaringClass().getSimpleName() + '.' + + methodsWithAnnotation[0].getName()); + assertEquals("Foo.doIt", + methodsWithAnnotation[1].getDeclaringClass().getSimpleName() + '.' + + methodsWithAnnotation[1].getName()); + } + + @Test + public void testGetMethodsWithAnnotationNotSearchSupersAndNotIgnoreAccess() throws NoSuchMethodException { + assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class, Annotated.class, + false, false)); + + final Method[] methodsWithAnnotation = MethodUtils.getMethodsWithAnnotation(PublicChild.class, Annotated.class, + false, false); + assertEquals(1, methodsWithAnnotation.length); + assertEquals("PublicChild.publicAnnotatedMethod", + methodsWithAnnotation[0].getDeclaringClass().getSimpleName() + '.' + + methodsWithAnnotation[0].getName()); + } + + @Test + public void testGetAnnotationSearchSupersAndIgnoreAccess() throws NoSuchMethodException { + assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, true, + true)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, + true, true)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), + Annotated.class, true, true)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"), + Annotated.class, true, true)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"), + Annotated.class, true, true)); + } + + @Test + public void testGetAnnotationNotSearchSupersButIgnoreAccess() throws NoSuchMethodException { + assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, false, + true)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, + false, true)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), + Annotated.class, false, true)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"), + Annotated.class, false, true)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"), + Annotated.class, false, true)); + } + + @Test + public void testGetAnnotationSearchSupersButNotIgnoreAccess() throws NoSuchMethodException { + assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, true, + false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, + true, false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), + Annotated.class, true, false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"), + Annotated.class, true, false)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"), + Annotated.class, true, false)); + } + + @Test + public void testGetAnnotationNotSearchSupersAndNotIgnoreAccess() throws NoSuchMethodException { + assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, false, + false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, + false, false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), + Annotated.class, false, false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getDeclaredMethod("privateAnnotatedMethod"), + Annotated.class, false, false)); + assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("publicAnnotatedMethod"), + Annotated.class, false, false)); } @Test(expected = IllegalArgumentException.class) @@ -741,6 +844,22 @@ public void testGetMethodsListWithAnnotationIllegalArgumentException3() { MethodUtils.getMethodsListWithAnnotation(null, null); } + @Test(expected = IllegalArgumentException.class) + public void testGetAnnotationIllegalArgumentException1() { + MethodUtils.getAnnotation(FieldUtilsTest.class.getDeclaredMethods()[0], null, true, + true); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetAnnotationIllegalArgumentException2() { + MethodUtils.getAnnotation(null, Annotated.class, true, true); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetAnnotationIllegalArgumentException3() { + MethodUtils.getAnnotation(null, null, true, true); + } + private void expectMatchingAccessibleMethodParameterTypes(final Class cls, final String methodName, final Class[] requestTypes, final Class[] actualTypes) { final Method m = MethodUtils.getMatchingAccessibleMethod(cls, methodName, diff --git a/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java b/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java index 0fc0239e6cb..fab1867bdaa 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java +++ b/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java @@ -22,10 +22,14 @@ public class PublicChild extends Parent { static final String VALUE = "child"; @Override - protected void parentProtectedAnnotatedMethod() { + public void parentProtectedAnnotatedMethod() { } @Annotated private void privateAnnotatedMethod() { } + + @Annotated + public void publicAnnotatedMethod() { + } } From b79fe6f9e350edb3086c44220968ae3dfcc0d2cb Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Wed, 29 Mar 2017 09:42:35 +0430 Subject: [PATCH 4/6] LANG-1317: Increases coverall's coverage --- .../commons/lang3/reflect/MethodUtilsTest.java | 16 ++++++++-------- .../commons/lang3/reflect/testbed/Parent.java | 3 +++ .../lang3/reflect/testbed/PublicChild.java | 4 ++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java index 7c8489f687a..15d7cd766e0 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java @@ -747,8 +747,8 @@ public void testGetMethodsWithAnnotationNotSearchSupersAndNotIgnoreAccess() thro @Test public void testGetAnnotationSearchSupersAndIgnoreAccess() throws NoSuchMethodException { - assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, true, - true)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"), + Annotated.class, true, true)); assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, true, true)); assertNotNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), @@ -761,8 +761,8 @@ public void testGetAnnotationSearchSupersAndIgnoreAccess() throws NoSuchMethodEx @Test public void testGetAnnotationNotSearchSupersButIgnoreAccess() throws NoSuchMethodException { - assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, false, - true)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"), + Annotated.class, false, true)); assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, false, true)); assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), @@ -775,8 +775,8 @@ public void testGetAnnotationNotSearchSupersButIgnoreAccess() throws NoSuchMetho @Test public void testGetAnnotationSearchSupersButNotIgnoreAccess() throws NoSuchMethodException { - assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, true, - false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"), + Annotated.class, true, false)); assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, true, false)); assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), @@ -789,8 +789,8 @@ public void testGetAnnotationSearchSupersButNotIgnoreAccess() throws NoSuchMetho @Test public void testGetAnnotationNotSearchSupersAndNotIgnoreAccess() throws NoSuchMethodException { - assertNull(MethodUtils.getAnnotation(Object.class.getDeclaredMethods()[0], Annotated.class, false, - false)); + assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentNotAnnotatedMethod"), + Annotated.class, false, false)); assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("doIt"), Annotated.class, false, false)); assertNull(MethodUtils.getAnnotation(PublicChild.class.getMethod("parentProtectedAnnotatedMethod"), diff --git a/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java b/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java index 007ea65292d..eeee5e398ef 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java +++ b/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java @@ -32,4 +32,7 @@ public void doIt() { @Annotated protected void parentProtectedAnnotatedMethod() { } + + public void parentNotAnnotatedMethod() { + } } diff --git a/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java b/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java index fab1867bdaa..ce2a1ec0465 100644 --- a/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java +++ b/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java @@ -25,6 +25,10 @@ public class PublicChild extends Parent { public void parentProtectedAnnotatedMethod() { } + @Override + public void parentNotAnnotatedMethod() { + } + @Annotated private void privateAnnotatedMethod() { } From 90e252f571377cac39f5e2a3fc73749f589c24de Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Thu, 20 Apr 2017 23:34:26 +0430 Subject: [PATCH 5/6] LANG-1317 Removes the priority parameter of getAllSuperclassesAndInterfaces --- .../org/apache/commons/lang3/ClassUtils.java | 13 ++--------- .../commons/lang3/reflect/MethodUtils.java | 6 ++--- .../apache/commons/lang3/ClassUtilsTest.java | 22 +++---------------- 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java index 34550a67e3a..f7ab8d6bbcb 100644 --- a/src/main/java/org/apache/commons/lang3/ClassUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java @@ -52,14 +52,6 @@ public enum Interfaces { INCLUDE, EXCLUDE } - /** - * Inclusivity literals for {@link #getAllSuperclassesAndInterfaces(Class, Priority)}. - * @since 3.6 - */ - public enum Priority { - SUPERCLASS, INTERFACE - } - /** * The package separator character: '.' == {@value}. */ @@ -470,11 +462,10 @@ public static List> getAllInterfaces(final Class cls) { * from interfaces, and so on in a breadth first way.

* * @param cls the class to look up, may be {@code null} - * @param p determines what to peek in same breadth, the superclass or interface * @return the {@code List} of superclasses in order going up from this one * {@code null} if null input */ - public static List> getAllSuperclassesAndInterfaces(final Class cls, Priority p) { + public static List> getAllSuperclassesAndInterfaces(final Class cls) { if (cls == null) { return null; } @@ -496,7 +487,7 @@ public static List> getAllSuperclassesAndInterfaces(final Class cls, } else if (sci < ifi) { acls = allSuperclasses.get(sci++); } else { - acls = (p == Priority.SUPERCLASS ? allSuperclasses.get(sci++) : allInterfaces.get(ifi++)); + acls = allInterfaces.get(ifi++); } classes.add(acls); } diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java index 0d54d99ff78..91fd7fe21e9 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java @@ -904,8 +904,8 @@ public static List getMethodsListWithAnnotation(final Class cls, Validate.isTrue(cls != null, "The class must not be null"); Validate.isTrue(annotationCls != null, "The annotation class must not be null"); - List> classes = (searchSupers ? ClassUtils.getAllSuperclassesAndInterfaces(cls, - ClassUtils.Priority.INTERFACE) : new ArrayList>()); + List> classes = (searchSupers ? ClassUtils.getAllSuperclassesAndInterfaces(cls) + : new ArrayList>()); classes.add(0, cls); final List annotatedMethods = new ArrayList<>(); for (Class acls : classes) { @@ -954,7 +954,7 @@ public static
A getAnnotation(final Method method, final if(annotation == null && searchSupers) { Class mcls = method.getDeclaringClass(); - List> classes = ClassUtils.getAllSuperclassesAndInterfaces(mcls, ClassUtils.Priority.INTERFACE); + List> classes = ClassUtils.getAllSuperclassesAndInterfaces(mcls); for (Class acls : classes) { Method equivalentMethod; try { diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java index a956638ce88..7c5e457a220 100644 --- a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java @@ -282,8 +282,8 @@ public void test_getAllInterfaces_Class() { } @Test - public void test_getAllSuperclassesAndInterfacesPriorityINTERFACE_Class() { - final List list = ClassUtils.getAllSuperclassesAndInterfaces(CY.class, ClassUtils.Priority.INTERFACE); + public void test_getAllSuperclassesAndInterfaces_Class() { + final List list = ClassUtils.getAllSuperclassesAndInterfaces(CY.class); assertEquals(8, list.size()); assertEquals(IB.class, list.get(0)); assertEquals(CX.class, list.get(1)); @@ -294,23 +294,7 @@ public void test_getAllSuperclassesAndInterfacesPriorityINTERFACE_Class() { assertEquals(IF.class, list.get(6)); assertEquals(IA.class, list.get(7)); - assertEquals(null, ClassUtils.getAllSuperclassesAndInterfaces(null, ClassUtils.Priority.INTERFACE)); - } - - @Test - public void test_getAllSuperclassesAndInterfacesPrioritySUPERCLASS_Class() { - final List list = ClassUtils.getAllSuperclassesAndInterfaces(CY.class, ClassUtils.Priority.SUPERCLASS); - assertEquals(8, list.size()); - assertEquals(CX.class, list.get(0)); - assertEquals(IB.class, list.get(1)); - assertEquals(Object.class, list.get(2)); - assertEquals(IC.class, list.get(3)); - assertEquals(ID.class, list.get(4)); - assertEquals(IE.class, list.get(5)); - assertEquals(IF.class, list.get(6)); - assertEquals(IA.class, list.get(7)); - - assertEquals(null, ClassUtils.getAllSuperclassesAndInterfaces(null, ClassUtils.Priority.SUPERCLASS)); + assertEquals(null, ClassUtils.getAllSuperclassesAndInterfaces(null)); } private static interface IA { From 6aee4203260f93cf4511779e8078952e90d6bb6f Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Fri, 21 Apr 2017 11:46:17 +0430 Subject: [PATCH 6/6] LANG-1317 Removes getAllSuperclassesAndInterfaces to MethodUtils as a private method --- .../org/apache/commons/lang3/ClassUtils.java | 38 ---------------- .../commons/lang3/reflect/MethodUtils.java | 44 ++++++++++++++++++- .../apache/commons/lang3/ClassUtilsTest.java | 16 ------- 3 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java index f7ab8d6bbcb..012d2a6e1a6 100644 --- a/src/main/java/org/apache/commons/lang3/ClassUtils.java +++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java @@ -456,44 +456,6 @@ public static List> getAllInterfaces(final Class cls) { return new ArrayList<>(interfacesFound); } - /** - *

Gets a combination of {@link #getAllSuperclasses}(Class)} and - * {@link #getAllInterfaces}(Class)}, one from superclasses, one - * from interfaces, and so on in a breadth first way.

- * - * @param cls the class to look up, may be {@code null} - * @return the {@code List} of superclasses in order going up from this one - * {@code null} if null input - */ - public static List> getAllSuperclassesAndInterfaces(final Class cls) { - if (cls == null) { - return null; - } - - final List> classes = new ArrayList<>(); - List> allSuperclasses = ClassUtils.getAllSuperclasses(cls); - int sci = 0; - List> allInterfaces = ClassUtils.getAllInterfaces(cls); - int ifi = 0; - while (ifi < allInterfaces.size() || - sci < allSuperclasses.size()) { - Class acls; - if (ifi >= allInterfaces.size()) { - acls = allSuperclasses.get(sci++); - } else if (sci >= allSuperclasses.size()) { - acls = allInterfaces.get(ifi++); - } else if (ifi < sci) { - acls = allInterfaces.get(ifi++); - } else if (sci < ifi) { - acls = allSuperclasses.get(sci++); - } else { - acls = allInterfaces.get(ifi++); - } - classes.add(acls); - } - return classes; - } - /** * Get the interfaces for the specified class. * diff --git a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java index 91fd7fe21e9..d0a9d3f8a88 100644 --- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java +++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java @@ -904,7 +904,7 @@ public static List getMethodsListWithAnnotation(final Class cls, Validate.isTrue(cls != null, "The class must not be null"); Validate.isTrue(annotationCls != null, "The annotation class must not be null"); - List> classes = (searchSupers ? ClassUtils.getAllSuperclassesAndInterfaces(cls) + List> classes = (searchSupers ? getAllSuperclassesAndInterfaces(cls) : new ArrayList>()); classes.add(0, cls); final List annotatedMethods = new ArrayList<>(); @@ -954,7 +954,7 @@ public static
A getAnnotation(final Method method, final if(annotation == null && searchSupers) { Class mcls = method.getDeclaringClass(); - List> classes = ClassUtils.getAllSuperclassesAndInterfaces(mcls); + List> classes = getAllSuperclassesAndInterfaces(mcls); for (Class acls : classes) { Method equivalentMethod; try { @@ -973,4 +973,44 @@ public static A getAnnotation(final Method method, final return annotation; } + + /** + *

Gets a combination of {@link ClassUtils#getAllSuperclasses}(Class)} and + * {@link ClassUtils#getAllInterfaces}(Class)}, one from superclasses, one + * from interfaces, and so on in a breadth first way.

+ * + * @param cls the class to look up, may be {@code null} + * @return the combined {@code List} of superclasses and interfaces in order + * going up from this one + * {@code null} if null input + * @since 3.6 + */ + private static List> getAllSuperclassesAndInterfaces(final Class cls) { + if (cls == null) { + return null; + } + + final List> classes = new ArrayList<>(); + List> allSuperclasses = ClassUtils.getAllSuperclasses(cls); + int sci = 0; + List> allInterfaces = ClassUtils.getAllInterfaces(cls); + int ifi = 0; + while (ifi < allInterfaces.size() || + sci < allSuperclasses.size()) { + Class acls; + if (ifi >= allInterfaces.size()) { + acls = allSuperclasses.get(sci++); + } else if (sci >= allSuperclasses.size()) { + acls = allInterfaces.get(ifi++); + } else if (ifi < sci) { + acls = allInterfaces.get(ifi++); + } else if (sci < ifi) { + acls = allSuperclasses.get(sci++); + } else { + acls = allInterfaces.get(ifi++); + } + classes.add(acls); + } + return classes; + } } diff --git a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java index 7c5e457a220..744aaa8377a 100644 --- a/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ClassUtilsTest.java @@ -281,22 +281,6 @@ public void test_getAllInterfaces_Class() { assertEquals(null, ClassUtils.getAllInterfaces(null)); } - @Test - public void test_getAllSuperclassesAndInterfaces_Class() { - final List list = ClassUtils.getAllSuperclassesAndInterfaces(CY.class); - assertEquals(8, list.size()); - assertEquals(IB.class, list.get(0)); - assertEquals(CX.class, list.get(1)); - assertEquals(IC.class, list.get(2)); - assertEquals(Object.class, list.get(3)); - assertEquals(ID.class, list.get(4)); - assertEquals(IE.class, list.get(5)); - assertEquals(IF.class, list.get(6)); - assertEquals(IA.class, list.get(7)); - - assertEquals(null, ClassUtils.getAllSuperclassesAndInterfaces(null)); - } - private static interface IA { } private static interface IB {