diff --git a/TODO b/TODO index 701fbd3..4600634 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,5 @@ To Do: * Implement "DriverFor" support - * Scanning packages for annotated components * General usage documentation * Uncouple component-provided names from the library diff --git a/src/main/java/info/ebenoit/ebul/cmp/NewComponentInfo.java b/src/main/java/info/ebenoit/ebul/cmp/NewComponentInfo.java index 7078bad..4bc6a9a 100644 --- a/src/main/java/info/ebenoit/ebul/cmp/NewComponentInfo.java +++ b/src/main/java/info/ebenoit/ebul/cmp/NewComponentInfo.java @@ -1,6 +1,7 @@ package info.ebenoit.ebul.cmp; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -15,7 +16,9 @@ import info.ebenoit.ebul.func.ThrowingBiConsumer; import info.ebenoit.ebul.func.ThrowingConsumer; import info.ebenoit.ebul.func.ThrowingSupplier; import info.ebenoit.ebul.reflection.Annotations; +import info.ebenoit.ebul.reflection.Classes; import info.ebenoit.ebul.reflection.MemberFinder; +import info.ebenoit.ebul.reflection.PackageScanner; @@ -55,6 +58,39 @@ public final class NewComponentInfo< T > } + /** + * Scans a package for classes that correspond to annotated components and processes them using + * {@link #fromClass(Class)} + * + * @param name + * the name of the package to scan + * @param recursive + * true if child packages are to be scanned as well + * @return the list of component registration records + * @throws IOException + * if an I/O error occurs while scanning the packages + * @throws ClassNotFoundException + * if one of the classes cannot be loaded + * @throws ComponentDefinitionException + * if one of the loaded definitions is invalid + */ + public static ArrayList< NewComponentInfo< ? > > scanPackage( final String name , final boolean recursive ) + throws IOException , ClassNotFoundException , ComponentDefinitionException + { + final PackageScanner packageScanner = new PackageScanner( name , recursive ); + final ArrayList< Class< ? > > classes = packageScanner.findClasses( // + c -> ( ( c.getModifiers( ) & ( Modifier.ABSTRACT | Modifier.INTERFACE ) ) == 0 + && Classes.hasAncestorMatching( c , x -> x.isAnnotationPresent( Component.class ) , true ) ) ); + + final ArrayList< NewComponentInfo< ? > > results = new ArrayList< >( ); + final int n = classes.size( ); + for ( int i = 0 ; i < n ; i++ ) { + results.add( NewComponentInfo.fromClass( classes.get( i ) ) ); + } + return results; + } + + /** * Creates a new component information record based on a component's annotated class. * @@ -540,7 +576,7 @@ public final class NewComponentInfo< T > } @SuppressWarnings( "unchecked" ) - ThrowingBiConsumer< Object , Object > ci = (ThrowingBiConsumer< Object , Object >) injector; + final ThrowingBiConsumer< Object , Object > ci = (ThrowingBiConsumer< Object , Object >) injector; injectors.add( ci ); this.dependencies.add( dependency ); diff --git a/src/test/java/info/ebenoit/ebul/cmp/TestNewComponentInfo.java b/src/test/java/info/ebenoit/ebul/cmp/TestNewComponentInfo.java index 5206ae8..f21da50 100644 --- a/src/test/java/info/ebenoit/ebul/cmp/TestNewComponentInfo.java +++ b/src/test/java/info/ebenoit/ebul/cmp/TestNewComponentInfo.java @@ -5,12 +5,20 @@ import org.junit.Test; import info.ebenoit.ebul.func.ThrowingBiConsumer; import info.ebenoit.ebul.func.ThrowingConsumer; +import test.inherited.CBase; +import test.inherited.CChild; +import test.inherited.CChildOfAbstract; +import test.interfaces.CImplementation; +import test.simple.CSimple; import static org.junit.Assert.*; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; @@ -889,4 +897,51 @@ public class TestNewComponentInfo injector.accept( cmp , inject ); assertSame( inject , cmp.dependency ); } + + + /** + * Test: {@link NewComponentInfo#scanPackage(String, boolean)} finds a simple {@link Component}-annotated class + */ + @Test + public void testScanPackageSimple( ) + throws ComponentDefinitionException , ClassNotFoundException , IOException + { + ArrayList< NewComponentInfo< ? > > result = NewComponentInfo.scanPackage( "test.simple" , false ); + assertEquals( 1 , result.size( ) ); + assertSame( CSimple.class , result.get( 0 ).getSupplier( ).get( ).getClass( ) ); + } + + + /** + * Test: {@link NewComponentInfo#scanPackage(String, boolean)} finds components that inherit each other, ignoring + * abstract classes + */ + @Test + public void testScanPackageInheritance( ) + throws ComponentDefinitionException , ClassNotFoundException , IOException + { + ArrayList< NewComponentInfo< ? > > result = NewComponentInfo.scanPackage( "test.inherited" , false ); + assertEquals( 3 , result.size( ) ); + + Set< ? > classes = result.stream( ).map( x -> x.getSupplier( ).get( ).getClass( ) ) + .collect( Collectors.toSet( ) ); + assertTrue( classes.contains( CBase.class ) ); + assertTrue( classes.contains( CChild.class ) ); + assertTrue( classes.contains( CChildOfAbstract.class ) ); + } + + + /** + * Test: {@link NewComponentInfo#scanPackage(String, boolean)} finds components defined by implementing a + * {@link Component}-annotated interface + */ + @Test + public void testScanPackageInterfaces( ) + throws ComponentDefinitionException , ClassNotFoundException , IOException + { + ArrayList< NewComponentInfo< ? > > result = NewComponentInfo.scanPackage( "test.interfaces" , false ); + assertEquals( 1 , result.size( ) ); + + assertSame( CImplementation.class , result.get( 0 ).getSupplier( ).get( ).getClass( ) ); + } } diff --git a/src/test/java/test/inherited/CAbstractBase.java b/src/test/java/test/inherited/CAbstractBase.java new file mode 100644 index 0000000..3e63359 --- /dev/null +++ b/src/test/java/test/inherited/CAbstractBase.java @@ -0,0 +1,9 @@ +package test.inherited; + +import info.ebenoit.ebul.cmp.Component; + +@Component +public abstract class CAbstractBase +{ + // EMPTY +} diff --git a/src/test/java/test/inherited/CAbstractChild.java b/src/test/java/test/inherited/CAbstractChild.java new file mode 100644 index 0000000..2529376 --- /dev/null +++ b/src/test/java/test/inherited/CAbstractChild.java @@ -0,0 +1,8 @@ +package test.inherited; + + +public abstract class CAbstractChild + extends CBase +{ + // EMPTY +} diff --git a/src/test/java/test/inherited/CBase.java b/src/test/java/test/inherited/CBase.java new file mode 100644 index 0000000..45ab14b --- /dev/null +++ b/src/test/java/test/inherited/CBase.java @@ -0,0 +1,12 @@ +package test.inherited; + + +import info.ebenoit.ebul.cmp.Component; + + + +@Component +public class CBase +{ + // EMPTY +} diff --git a/src/test/java/test/inherited/CChild.java b/src/test/java/test/inherited/CChild.java new file mode 100644 index 0000000..15150fd --- /dev/null +++ b/src/test/java/test/inherited/CChild.java @@ -0,0 +1,8 @@ +package test.inherited; + + +public class CChild + extends CBase +{ + // EMPTY +} diff --git a/src/test/java/test/inherited/CChildOfAbstract.java b/src/test/java/test/inherited/CChildOfAbstract.java new file mode 100644 index 0000000..62d42c5 --- /dev/null +++ b/src/test/java/test/inherited/CChildOfAbstract.java @@ -0,0 +1,8 @@ +package test.inherited; + + +public class CChildOfAbstract + extends CAbstractBase +{ + +} diff --git a/src/test/java/test/interfaces/CImplementation.java b/src/test/java/test/interfaces/CImplementation.java new file mode 100644 index 0000000..8257161 --- /dev/null +++ b/src/test/java/test/interfaces/CImplementation.java @@ -0,0 +1,8 @@ +package test.interfaces; + + +public class CImplementation + implements CInterface +{ + // EMPTY +} diff --git a/src/test/java/test/interfaces/CInterface.java b/src/test/java/test/interfaces/CInterface.java new file mode 100644 index 0000000..cd4e89a --- /dev/null +++ b/src/test/java/test/interfaces/CInterface.java @@ -0,0 +1,12 @@ +package test.interfaces; + + +import info.ebenoit.ebul.cmp.Component; + + + +@Component +public interface CInterface +{ + // EMPTY +} diff --git a/src/test/java/test/simple/CSimple.java b/src/test/java/test/simple/CSimple.java new file mode 100644 index 0000000..31abae7 --- /dev/null +++ b/src/test/java/test/simple/CSimple.java @@ -0,0 +1,12 @@ +package test.simple; + + +import info.ebenoit.ebul.cmp.Component; + + + +@Component +public class CSimple +{ + // EMPTY +}