Skip to content

Commit

Permalink
LANG-1317: More parametric and readable, refactored duplicate code an…
Browse files Browse the repository at this point in the history
…d more unit tests
  • Loading branch information
yasserzamani committed Mar 28, 2017
1 parent e49a3a2 commit 7390433
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 86 deletions.
47 changes: 47 additions & 0 deletions src/main/java/org/apache/commons/lang3/ClassUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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: <code>'&#x2e;' == {@value}</code>.
*/
Expand Down Expand Up @@ -456,6 +464,45 @@ public static List<Class<?>> getAllInterfaces(final Class<?> cls) {
return new ArrayList<>(interfacesFound);
}

/**
* <p>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.</p>
*
* @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<Class<?>> getAllSuperclassesAndInterfaces(final Class<?> cls, Priority p) {
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 = (p == Priority.SUPERCLASS ? allSuperclasses.get(sci++) : allInterfaces.get(ifi++));
}
classes.add(acls);
}
return classes;
}

/**
* Get the interfaces for the specified class.
*
Expand Down
133 changes: 57 additions & 76 deletions src/main/java/org/apache/commons/lang3/reflect/MethodUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ public static Set<Method> 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
Expand All @@ -843,12 +843,11 @@ public static Set<Method> getOverrideHierarchy(final Method method, final Interf
* @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
Expand All @@ -859,73 +858,59 @@ public static Method[] getMethodsWithAnnotation(final Class<?> cls, final Class<
* @since 3.4
*/
public static List<Method> getMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> 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<Method> annotatedMethods = new ArrayList<>();
for (final Method method : allMethods) {
if (method.getAnnotation(annotationCls) != null) {
annotatedMethods.add(method);
}
}
return annotatedMethods;
return getMethodsListWithAnnotation(cls, annotationCls, false, false);
}

/**
* <p>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.</p>
* 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<? extends Annotation> annotationCls) {
final List<Method> annotatedMethodsList = findMethodsListWithAnnotation(cls, annotationCls);
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()]);
}

/**
* <p>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.</p>
* 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> findMethodsListWithAnnotation(final Class<?> cls, final Class<? extends Annotation> annotationCls) {
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");
List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(cls);
allSuperclasses.add(0, cls);
int sci = 0;
List<Class<?>> allInterfaces = ClassUtils.getAllInterfaces(cls);
int ifi = 0;
List<Class<?>> classes = (searchSupers ? ClassUtils.getAllSuperclassesAndInterfaces(cls,
ClassUtils.Priority.INTERFACE) : new ArrayList<Class<?>>());
classes.add(0, cls);
final List<Method> 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);
}
Expand All @@ -935,57 +920,53 @@ else if (sci <= ifi) {
}

/**
* <p>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.</p>
* <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
* @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 extends Annotation> A findAnnotation(final Method method, final Class<A> annotationCls) {
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) {
if(annotation == null && searchSupers) {
Class<?> mcls = method.getDeclaringClass();
List<Class<?>> allSuperclasses = ClassUtils.getAllSuperclasses(mcls);
int sci = 0;
List<Class<?>> 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<Class<?>> 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;
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/test/java/org/apache/commons/lang3/ClassUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 7390433

Please sign in to comment.