diff --git a/src/main/java/info/ebenoit/ebul/reflection/Annotations.java b/src/main/java/info/ebenoit/ebul/reflection/Annotations.java new file mode 100644 index 0000000..f1d28cc --- /dev/null +++ b/src/main/java/info/ebenoit/ebul/reflection/Annotations.java @@ -0,0 +1,200 @@ +package info.ebenoit.ebul.reflection; + + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.Consumer; + + + +/** + * Helpers to deal with annotated methods, fields or classes. + * + * @author E. BenoƮt + */ +public final class Annotations +{ + + /** + * Finds all declared methods that have a specific annotation in a given class + * + * @param target + * the class to examine + * @param annotation + * the annotation that must be present + * @return the list of methods that have the specified annotation + */ + public static final ArrayList< Method > findAnnotatedMethods( final Class< ? > target , + final Class< ? extends Annotation > annotation ) + { + final ArrayList< Method > methods = new ArrayList< >( ); + Annotations.findAnnotatedMethods( methods , target , annotation ); + return methods; + } + + + /** + * Finds all declared methods that have a specific annotation in a given class and adds them to an existing + * {@link Collection}. + * + * @param output + * the collection to which methods will be added + * @param target + * the class to examine + * @param annotation + * the annotation that must be present + * @return the output collection + */ + public static final Collection< Method > findAnnotatedMethods( final Collection< Method > output , + final Class< ? > target , final Class< ? extends Annotation > annotation ) + { + for ( final Method m : target.getDeclaredMethods( ) ) { + if ( m.isAnnotationPresent( annotation ) ) { + output.add( m ); + } + } + return output; + } + + + /** + * Finds all declared fields that have a specific annotation in a given class + * + * @param target + * the class to examine + * @param annotation + * the annotation that must be present + * @return the list of fields that have the specified annotation + */ + public static final ArrayList< Field > findAnnotatedFields( final Class< ? > target , + final Class< ? extends Annotation > annotation ) + { + final ArrayList< Field > fields = new ArrayList< >( ); + Annotations.findAnnotatedFields( fields , target , annotation ); + return fields; + } + + + /** + * Finds all declared fields that have a specific annotation in a given class and adds them to an existing + * {@link Collection}. + * + * @param output + * the output collection + * @param target + * the class to examine + * @param annotation + * the annotation that must be present + * @return the output collection + */ + public static final Collection< Field > findAnnotatedFields( final Collection< Field > output , + final Class< ? > target , final Class< ? extends Annotation > annotation ) + { + for ( final Field f : target.getDeclaredFields( ) ) { + if ( f.isAnnotationPresent( annotation ) ) { + output.add( f ); + } + } + return output; + } + + + /** + * Applies a function to declared methods with a specific annotation + * + * @param target + * the class to examine + * @param annotation + * the annotation that must be present + * @param function + * the function to apply to the methods + */ + public static final void applyToAnnotatedMethods( final Class< ? > target , + final Class< ? extends Annotation > annotation , final Consumer< Method > function ) + { + for ( final Method m : target.getDeclaredMethods( ) ) { + if ( m.isAnnotationPresent( annotation ) ) { + function.accept( m ); + } + } + } + + + /** + * Applies a function to declared fields with a specific annotation + * + * @param target + * the class to examine + * @param annotation + * the annotation that must be present + * @param function + * the function to apply to the fields + */ + public static final void applyToAnnotatedFields( final Class< ? > target , + final Class< ? extends Annotation > annotation , final Consumer< Field > function ) + { + for ( final Field f : target.getDeclaredFields( ) ) { + if ( f.isAnnotationPresent( annotation ) ) { + function.accept( f ); + } + } + } + + + /** + * Finds the closest class in a hierarchy that has the specified annotation. + *

+ * For example, assuming class A extends B which extends C which extends D, both B and C having the annotation, this + * method will return B if called on A or B. + * + * @param klass + * the class to examine + * @param annotation + * the annotation to look for + * @return the closest class with the specified annotation in the hierarchy, or null if the annotation + * is not present. + */ + public static final < T > Class< ? super T > findClosestClass( final Class< T > klass , + final Class< ? extends Annotation > annotation ) + { + Class< ? super T > current = klass; + while ( current != null ) { + if ( current.isAnnotationPresent( annotation ) ) { + return current; + } + current = current.getSuperclass( ); + } + return null; + } + + + /** + * Finds the farthest class in a hierarchy that has the specified annotation. + *

+ * For example, assuming class A extends B which extends C which extends D, both B and C having the annotation, this + * method will return C if called on A or B. + * + * @param klass + * the class to examine + * @param annotation + * the annotation to look for + * @return the farthest class with the specified annotation in the hierarchy, or null if the annotation + * is not present. + */ + public static final < T > Class< ? super T > findFarthestClass( final Class< T > klass , + final Class< ? extends Annotation > annotation ) + { + Class< ? super T > found = null , current = klass; + while ( current != null ) { + if ( current.isAnnotationPresent( annotation ) ) { + found = current; + } + current = current.getSuperclass( ); + } + return found; + } + +}