LANG-1317: Adds MethodUtils#findAnnotation and extend MethodUtils#getMethodsWithAnnotation for non-public, super-class and interface methods (closes #261)
This commit is contained in:
parent
859224ffad
commit
46007c151e
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -21,5 +21,6 @@ package org.apache.commons.lang3.reflect.testbed;
|
|||
public interface Foo {
|
||||
public static final String VALUE = "foo";
|
||||
|
||||
@Annotated
|
||||
void doIt();
|
||||
}
|
||||
|
|
|
@ -28,4 +28,11 @@ class Parent implements Foo {
|
|||
@Override
|
||||
public void doIt() {
|
||||
}
|
||||
|
||||
@Annotated
|
||||
protected void parentProtectedAnnotatedMethod() {
|
||||
}
|
||||
|
||||
public void parentNotAnnotatedMethod() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue