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 ) ); + } + }