diff --git a/src/main/java/info/ebenoit/ebul/reflection/Classes.java b/src/main/java/info/ebenoit/ebul/reflection/Classes.java
index d179167..10bfff0 100644
--- a/src/main/java/info/ebenoit/ebul/reflection/Classes.java
+++ b/src/main/java/info/ebenoit/ebul/reflection/Classes.java
@@ -3,6 +3,7 @@ package info.ebenoit.ebul.reflection;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.function.Predicate;
@@ -16,7 +17,7 @@ public final class Classes
/**
* List all classes a class inherits from.
- *
+ *
* @param cls
* the class to examine
* @return the list of ancestors, including the class itself
@@ -35,7 +36,7 @@ public final class Classes
/**
* Find all types a class is compatible with: itself, its ancestors, and all implemented interfaces.
- *
+ *
* @param cls
* the class to examine
* @return the set of compatible types
@@ -54,8 +55,43 @@ public final class Classes
/**
- * Internal method used by {@link #getAllTypes(Class)} to add all interfaces recursively.
+ * Checks for a class (or optionally an interface) matching a predicate in a class hierarchy.
*
+ * @param klass
+ * the class to start checking from
+ * @param test
+ * the predicate
+ * @param interfaces
+ * true
if interfaces will be tested, false
if the test is to be applied to
+ * actual classes only
+ * @return true
if any class or interface matches the test, false
if none of them do.
+ */
+ public static boolean hasAncestorMatching( final Class< ? > klass , final Predicate< Class< ? > > test ,
+ final boolean interfaces )
+ {
+ if ( klass == null ) {
+ return false;
+ }
+
+ if ( test.test( klass ) ) {
+ return true;
+ }
+
+ if ( interfaces ) {
+ for ( final Class< ? > itf : klass.getInterfaces( ) ) {
+ if ( Classes.hasAncestorMatching( itf , test , true ) ) {
+ return true;
+ }
+ }
+ }
+
+ return Classes.hasAncestorMatching( klass.getSuperclass( ) , test , interfaces );
+ }
+
+
+ /**
+ * Internal method used by {@link #getAllTypes(Class)} to add all interfaces recursively.
+ *
* @param current
* the class or interface being examined
* @param found
diff --git a/src/test/java/info/ebenoit/ebul/reflection/TestClasses.java b/src/test/java/info/ebenoit/ebul/reflection/TestClasses.java
index e9e9b31..5ef2228 100644
--- a/src/test/java/info/ebenoit/ebul/reflection/TestClasses.java
+++ b/src/test/java/info/ebenoit/ebul/reflection/TestClasses.java
@@ -98,4 +98,48 @@ public class TestClasses
Assert.assertEquals( expected , result );
}
+
+ /**
+ * Test: {@link Classes#hasAncestorMatching(Class, java.util.function.Predicate, boolean)} finds class itself in
+ * hierarchy
+ */
+ @Test
+ public void testHasAncestorMatchingSelf( )
+ {
+ Assert.assertTrue( Classes.hasAncestorMatching( CTest4.class , c -> c == CTest4.class , false ) );
+ }
+
+
+ /**
+ * Test: {@link Classes#hasAncestorMatching(Class, java.util.function.Predicate, boolean)} finds top of class
+ * hierarchy
+ */
+ @Test
+ public void testHasAncestorMatchingTopOfHierarchy( )
+ {
+ Assert.assertTrue( Classes.hasAncestorMatching( CTest4.class , c -> c == Object.class , false ) );
+ }
+
+
+ /**
+ * Test: {@link Classes#hasAncestorMatching(Class, java.util.function.Predicate, boolean)} doesn't look at
+ * interfaces if told not to
+ */
+ @Test
+ public void testHasAncestorMatchingNoInterfaces( )
+ {
+ Assert.assertFalse( Classes.hasAncestorMatching( CTest5.class , c -> c == ITest1.class , false ) );
+ }
+
+
+ /**
+ * Test: {@link Classes#hasAncestorMatching(Class, java.util.function.Predicate, boolean)} looks at interfaces if
+ * told to
+ */
+ @Test
+ public void testHasAncestorMatchingInterfaces( )
+ {
+ Assert.assertTrue( Classes.hasAncestorMatching( CTest5.class , c -> c == ITest1.class , true ) );
+ }
+
}