You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by br...@apache.org on 2017/05/17 15:04:04 UTC
[20/50] [lang] LANG-1317: Adds MethodUtils#findAnnotation and extend
MethodUtils#getMethodsWithAnnotation for non-public,
super-class and interface methods (closes #261)
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/release
Commit: 46007c151e4cd79891eed4d289ff372ba39d5b8c
Parents: 859224f
Author: Yasser Zamani <ya...@live.com>
Authored: Sun Mar 26 01:49:46 2017 +0430
Committer: pascalschumacher <pa...@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() {
+ }
}