diff --git a/src/main/java/info/ebenoit/ebul/reflection/Classes.java b/src/main/java/info/ebenoit/ebul/reflection/Classes.java new file mode 100644 index 0000000..d179167 --- /dev/null +++ b/src/main/java/info/ebenoit/ebul/reflection/Classes.java @@ -0,0 +1,80 @@ +package info.ebenoit.ebul.reflection; + + +import java.util.ArrayList; +import java.util.HashSet; + + + +/** + * Utility methods to query classes and interfaces. + * + * @author E. BenoƮt + */ +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 + */ + public static < T > ArrayList< Class< ? super T > > getAncestors( final Class< T > cls ) + { + final ArrayList< Class< ? super T > > result = new ArrayList< >( ); + Class< ? super T > current = cls; + while ( current != null ) { + result.add( current ); + current = current.getSuperclass( ); + } + return result; + } + + + /** + * 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 + */ + public static HashSet< Class< ? > > getAllTypes( final Class< ? > cls ) + { + final HashSet< Class< ? > > found = new HashSet< >( ); + Class< ? > current = cls; + while ( current != null ) { + found.add( current ); + Classes.addInterfaces( current , found ); + current = current.getSuperclass( ); + } + return found; + } + + + /** + * Internal method used by {@link #getAllTypes(Class)} to add all interfaces recursively. + * + * @param current + * the class or interface being examined + * @param found + * the result + */ + private static void addInterfaces( final Class< ? > current , final HashSet< Class< ? > > found ) + { + if ( current == null ) { + return; + } + + final Class< ? >[] interfaces = current.getInterfaces( ); + final int len = interfaces.length; + for ( int i = 0 ; i < len ; i++ ) { + final Class< ? > itf = interfaces[ i ]; + if ( found.add( itf ) ) { + Classes.addInterfaces( itf , found ); + } + } + } + +} diff --git a/src/test/java/info/ebenoit/ebul/reflection/CTest6.java b/src/test/java/info/ebenoit/ebul/reflection/CTest6.java new file mode 100644 index 0000000..49302dd --- /dev/null +++ b/src/test/java/info/ebenoit/ebul/reflection/CTest6.java @@ -0,0 +1,9 @@ +package info.ebenoit.ebul.reflection; + + +public class CTest6 + extends CTest5 + implements ITest2 +{ + // EMPTY +} diff --git a/src/test/java/info/ebenoit/ebul/reflection/ITest2.java b/src/test/java/info/ebenoit/ebul/reflection/ITest2.java new file mode 100644 index 0000000..7172ede --- /dev/null +++ b/src/test/java/info/ebenoit/ebul/reflection/ITest2.java @@ -0,0 +1,7 @@ +package info.ebenoit.ebul.reflection; + +public interface ITest2 + extends ITest1 +{ + +} diff --git a/src/test/java/info/ebenoit/ebul/reflection/TestClasses.java b/src/test/java/info/ebenoit/ebul/reflection/TestClasses.java new file mode 100644 index 0000000..e9e9b31 --- /dev/null +++ b/src/test/java/info/ebenoit/ebul/reflection/TestClasses.java @@ -0,0 +1,101 @@ +package info.ebenoit.ebul.reflection; + + +import java.util.HashSet; + +import org.junit.Assert; +import org.junit.Test; + + + +/** Tests for {@link Classes} */ +public class TestClasses +{ + + /** Test: {@link Classes#getAncestors(Class)} called on Object.class */ + @Test + public void getAncestorsObject( ) + { + @SuppressWarnings( "unchecked" ) + final Class< ? super Object > expected[] = new Class[] { + Object.class + }; + final Class< ? super Object > result[] = Classes.getAncestors( Object.class ).toArray( expected ); + + Assert.assertArrayEquals( expected , result ); + } + + + /** Test: {@link Classes#getAncestors(Class)} called on an actual hierarchy */ + @Test + public void getAncestors( ) + { + @SuppressWarnings( "unchecked" ) + final Class< ? super CTest4 > expected[] = new Class[] { + CTest4.class , CTest3.class , CTest2.class , CTest1.class , Object.class + }; + final Class< ? super CTest4 > result[] = Classes.getAncestors( CTest4.class ).toArray( expected ); + + Assert.assertArrayEquals( expected , result ); + } + + + /** Test: {@link Classes#getAllTypes(Class)} called on Object.class */ + @Test + public void getAllTypesObject( ) + { + final Class< ? > expected[] = new Class[] { + Object.class + }; + final Class< ? > result[] = Classes.getAllTypes( Object.class ).toArray( expected ); + + Assert.assertArrayEquals( expected , result ); + } + + + /** Test: {@link Classes#getAllTypes(Class)} called on a hierarchy with no implemented interfaces */ + @Test + public void getAllTypesHierarchy( ) + { + final HashSet< Class< ? > > expected = new HashSet< Class< ? > >( ); + expected.add( Object.class ); + expected.add( CTest1.class ); + expected.add( CTest2.class ); + expected.add( CTest3.class ); + expected.add( CTest4.class ); + + final HashSet< Class< ? > > result = Classes.getAllTypes( CTest4.class ); + Assert.assertEquals( expected , result ); + } + + + /** Test: {@link Classes#getAllTypes(Class)} called on a hierarchy with one implemented interfaces */ + @Test + public void getAllTypesInterface( ) + { + final HashSet< Class< ? > > expected = new HashSet< Class< ? > >( ); + expected.add( Object.class ); + expected.add( ITest1.class ); + expected.add( CTest5.class ); + + final HashSet< Class< ? > > result = Classes.getAllTypes( CTest5.class ); + Assert.assertEquals( expected , result ); + } + + + /** Test: {@link Classes#getAllTypes(Class)} called on a hierarchy with redundant interfaces */ + @Test + public void getAllTypesInterfaces( ) + { + final HashSet< Class< ? > > expected = new HashSet< Class< ? > >( ); + expected.add( Object.class ); + expected.add( ITest1.class ); + expected.add( ITest2.class ); + expected.add( CTest5.class ); + expected.add( CTest6.class ); + + final HashSet< Class< ? > > result = Classes.getAllTypes( CTest6.class ); + Assert.assertEquals( expected , result ); + } + +}