commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pascalschumac...@apache.org
Subject [1/2] [lang] LANG-1317: Adds MethodUtils#findAnnotation and extend MethodUtils#getMethodsWithAnnotation for non-public, super-class and interface methods (closes #261)
Date Fri, 21 Apr 2017 08:45:38 GMT
Repository: commons-lang
Updated Branches:
  refs/heads/master 859224ffa -> 314b6b56b


LANG-1317: Adds MethodUtils#findAnnotation and extend MethodUtils#getMethodsWithAnnotation
for non-public, super-class and interface methods (closes #261)


Project: http://git-wip-us.apache.org/repos/asf/commons-lang/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-lang/commit/46007c15
Tree: http://git-wip-us.apache.org/repos/asf/commons-lang/tree/46007c15
Diff: http://git-wip-us.apache.org/repos/asf/commons-lang/diff/46007c15

Branch: refs/heads/master
Commit: 46007c151e4cd79891eed4d289ff372ba39d5b8c
Parents: 859224f
Author: Yasser Zamani <yasser.zamani@live.com>
Authored: Sun Mar 26 01:49:46 2017 +0430
Committer: pascalschumacher <pascalschumacher@gmx.net>
Committed: Fri Apr 21 10:44:41 2017 +0200

----------------------------------------------------------------------
 .../commons/lang3/reflect/MethodUtils.java      | 158 ++++++++++++++++++-
 .../commons/lang3/reflect/MethodUtilsTest.java  | 136 ++++++++++++++++
 .../commons/lang3/reflect/testbed/Foo.java      |   1 +
 .../commons/lang3/reflect/testbed/Parent.java   |   7 +
 .../lang3/reflect/testbed/PublicChild.java      |  16 ++
 5 files changed, 310 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-lang/blob/46007c15/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
----------------------------------------------------------------------
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 c6f9795..55b5cb1 100644
--- a/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
+++ b/src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
@@ -830,7 +830,7 @@ public class MethodUtils {
     }
 
     /**
-     * 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
@@ -841,12 +841,11 @@ public class MethodUtils {
      * @since 3.4
      */
     public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<?
extends Annotation> annotationCls) {
-        final List<Method> 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
@@ -857,16 +856,159 @@ public class MethodUtils {
      * @since 3.4
      */
     public static List<Method> getMethodsListWithAnnotation(final Class<?> cls,
final Class<? extends Annotation> annotationCls) {
+        return getMethodsListWithAnnotation(cls, annotationCls, false, false);
+    }
+
+    /**
+     * 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[] getMethodsWithAnnotation(final Class<?> cls, final Class<?
extends Annotation> annotationCls,
+                                                    boolean searchSupers, boolean ignoreAccess)
{
+        final List<Method> annotatedMethodsList = getMethodsListWithAnnotation(cls,
annotationCls, searchSupers,
+                ignoreAccess);
+        return annotatedMethodsList.toArray(new Method[annotatedMethodsList.size()]);
+    }
+
+    /**
+     * 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<Method> getMethodsListWithAnnotation(final Class<?> cls,
+                                                            final Class<? extends Annotation>
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");
-        final Method[] allMethods = cls.getMethods();
+        List<Class<?>> classes = (searchSupers ? getAllSuperclassesAndInterfaces(cls)
+                : new ArrayList<Class<?>>());
+        classes.add(0, cls);
         final List<Method> annotatedMethods = new ArrayList<>();
-        for (final Method method : allMethods) {
-            if (method.getAnnotation(annotationCls) != null) {
-                annotatedMethods.add(method);
+        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);
+                }
             }
         }
         return annotatedMethods;
     }
 
+    /**
+     * <p>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.</p>
+     *
+     * <p>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.</p>
+     * @param <A>
+     *            the annotation type
+     * @param method
+     *            the {@link Method} to query
+     * @param annotationCls
+     *            the {@link Annotation} to check if is present on the method
+     * @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 extends Annotation> A getAnnotation(final Method method, final
Class<A> 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 && searchSupers) {
+            Class<?> mcls = method.getDeclaringClass();
+            List<Class<?>> classes = getAllSuperclassesAndInterfaces(mcls);
+            for (Class<?> acls : classes) {
+                Method equivalentMethod;
+                try {
+                    equivalentMethod = (ignoreAccess ? acls.getDeclaredMethod(method.getName(),
method.getParameterTypes())
+                            : acls.getMethod(method.getName(), method.getParameterTypes()));
+                } catch (NoSuchMethodException e) {
+                    // If not found, just keep on search
+                    continue;
+                }
+                annotation = equivalentMethod.getAnnotation(annotationCls);
+                if (annotation != null) {
+                    break;
+                }
+            }
+        }
+
+        return annotation;
+    }
+
+    /**
+     * <p>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.</p>
+     *
+     * @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<Class<?>> getAllSuperclassesAndInterfaces(final Class<?>
cls) {
+        if (cls == null) {
+            return null;
+        }
+
+        final List<Class<?>> classes = new ArrayList<>();
+        List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
+        int sci = 0;
+        List<Class<?>> 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;
+    }
 }

http://git-wip-us.apache.org/repos/asf/commons-lang/blob/46007c15/src/test/java/org/apache/commons/lang3/reflect/MethodUtilsTest.java
----------------------------------------------------------------------
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 df57a13..15d7cd7 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.mutable.MutableObject;
 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,125 @@ public class MethodUtilsTest {
         assertThat(methodsWithAnnotation, hasItemInArray(MethodUtilsTest.class.getMethod("testGetMethodsListWithAnnotation")));
     }
 
+    @Test
+    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 testGetMethodsWithAnnotationNotSearchSupersButIgnoreAccess() throws NoSuchMethodException
{
+        assertArrayEquals(new Method[0], MethodUtils.getMethodsWithAnnotation(Object.class,
Annotated.class,
+                false, true));
+
+        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(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"),
+                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(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"),
+                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(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"),
+                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(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"),
+                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)
     public void testGetMethodsWithAnnotationIllegalArgumentException1() {
         MethodUtils.getMethodsWithAnnotation(FieldUtilsTest.class, null);
@@ -724,6 +844,22 @@ public class MethodUtilsTest {
         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,

http://git-wip-us.apache.org/repos/asf/commons-lang/blob/46007c15/src/test/java/org/apache/commons/lang3/reflect/testbed/Foo.java
----------------------------------------------------------------------
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 5511993..be24ac6 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 @@ package org.apache.commons.lang3.reflect.testbed;
 public interface Foo {
     public static final String VALUE = "foo";
 
+    @Annotated
     void doIt();
 }

http://git-wip-us.apache.org/repos/asf/commons-lang/blob/46007c15/src/test/java/org/apache/commons/lang3/reflect/testbed/Parent.java
----------------------------------------------------------------------
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 70447b4..eeee5e3 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,11 @@ class Parent implements Foo {
     @Override
     public void doIt() {
     }
+
+    @Annotated
+    protected void parentProtectedAnnotatedMethod() {
+    }
+
+    public void parentNotAnnotatedMethod() {
+    }
 }

http://git-wip-us.apache.org/repos/asf/commons-lang/blob/46007c15/src/test/java/org/apache/commons/lang3/reflect/testbed/PublicChild.java
----------------------------------------------------------------------
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 c91a06f..ce2a1ec 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,20 @@ package org.apache.commons.lang3.reflect.testbed;
  */
 public class PublicChild extends Parent {
     static final String VALUE = "child";
+
+    @Override
+    public void parentProtectedAnnotatedMethod() {
+    }
+
+    @Override
+    public void parentNotAnnotatedMethod() {
+    }
+
+    @Annotated
+    private void privateAnnotatedMethod() {
+    }
+
+    @Annotated
+    public void publicAnnotatedMethod() {
+    }
 }


Mime
View raw message