Member finder utility
This commit is contained in:
parent
d633aac37d
commit
722f071674
1 changed files with 351 additions and 0 deletions
351
src/main/java/info/ebenoit/ebul/reflection/MemberFinder.java
Normal file
351
src/main/java/info/ebenoit/ebul/reflection/MemberFinder.java
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
package info.ebenoit.ebul.reflection;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to find methods and fields in a class hierarchy.
|
||||||
|
* <p>
|
||||||
|
* This class can be used to look for specific members in a class hierarchy. It can be set up so that it also checks
|
||||||
|
* interfaces.
|
||||||
|
*
|
||||||
|
* @param <T>
|
||||||
|
* the type of members being examined
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:ebenoit@ebenoit.info">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class MemberFinder< T >
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Function to extract methods from a class */
|
||||||
|
public static final Function< Class< ? > , Method[] > METHOD_EXTRACTOR = c -> c.getMethods( );
|
||||||
|
/** Function to extract fields from a class */
|
||||||
|
public static final Function< Class< ? > , Field[] > FIELD_EXTRACTOR = c -> c.getFields( );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists methods matching some criteria
|
||||||
|
* <p>
|
||||||
|
* This method is a shortcut that configures a {@link MemberFinder} instance for methods, and applies it.
|
||||||
|
*
|
||||||
|
* @param target
|
||||||
|
* the class to examine
|
||||||
|
* @param methodFilter
|
||||||
|
* the method filtering function (may be <code>null</code>)
|
||||||
|
* @param classFilter
|
||||||
|
* the class filtering function (may be <code>null</code>)
|
||||||
|
* @param interfaces
|
||||||
|
* whether interfaces should be examined too
|
||||||
|
* @param classesFirst
|
||||||
|
* if interfaces are to be examined, <code>true</code> if superclasses should be examined first
|
||||||
|
* @return an array of methods matching the specified criteria
|
||||||
|
*/
|
||||||
|
public static final ArrayList< Method > getAllMethods( final Class< ? > target ,
|
||||||
|
final Predicate< Method > methodFilter , final Predicate< Class< ? > > classFilter ,
|
||||||
|
final boolean interfaces , final boolean classesFirst )
|
||||||
|
{
|
||||||
|
final ArrayList< Method > result = new ArrayList< >( );
|
||||||
|
MemberFinder.getAllMethods( result , target , methodFilter , classFilter , interfaces , classesFirst );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists methods matching some criteria
|
||||||
|
* <p>
|
||||||
|
* This method is a shortcut that configures a {@link MemberFinder} instance for methods, and applies it.
|
||||||
|
*
|
||||||
|
* @param output
|
||||||
|
* the collection to which matching methods will be added
|
||||||
|
* @param target
|
||||||
|
* the class to examine
|
||||||
|
* @param methodFilter
|
||||||
|
* the method filtering function (may be <code>null</code>)
|
||||||
|
* @param classFilter
|
||||||
|
* the class filtering function (may be <code>null</code>)
|
||||||
|
* @param interfaces
|
||||||
|
* whether interfaces should be examined too
|
||||||
|
* @param classesFirst
|
||||||
|
* if interfaces are to be examined, <code>true</code> if superclasses should be examined first
|
||||||
|
* @return the output collection to which methods matching the specified criteria were added
|
||||||
|
*/
|
||||||
|
public static final Collection< Method > getAllMethods( final Collection< Method > output ,
|
||||||
|
final Class< ? > target , final Predicate< Method > methodFilter ,
|
||||||
|
final Predicate< Class< ? > > classFilter , final boolean interfaces , final boolean classesFirst )
|
||||||
|
{
|
||||||
|
final MemberFinder< Method > mf = new MemberFinder< >( MemberFinder.METHOD_EXTRACTOR , interfaces );
|
||||||
|
mf.setClassesFirst( classesFirst );
|
||||||
|
mf.setClassFilter( classFilter );
|
||||||
|
mf.setMemberFilter( methodFilter );
|
||||||
|
mf.find( output , target );
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists fields matching some criteria
|
||||||
|
* <p>
|
||||||
|
* This method is a shortcut that configures a {@link MemberFinder} instance for fields, and applies it.
|
||||||
|
*
|
||||||
|
* @param target
|
||||||
|
* the class to examine
|
||||||
|
* @param fieldFilter
|
||||||
|
* the field filtering function (may be <code>null</code>)
|
||||||
|
* @param classFilter
|
||||||
|
* the class filtering function (may be <code>null</code>)
|
||||||
|
* @return an array of fields matching the specified criteria
|
||||||
|
*/
|
||||||
|
public static final ArrayList< Field > getAllFields( final Class< ? > target ,
|
||||||
|
final Predicate< Field > fieldFilter , final Predicate< Class< ? > > classFilter )
|
||||||
|
{
|
||||||
|
final ArrayList< Field > result = new ArrayList< >( );
|
||||||
|
MemberFinder.getAllFields( result , target , fieldFilter , classFilter );
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists fields matching some criteria
|
||||||
|
* <p>
|
||||||
|
* This method is a shortcut that configures a {@link MemberFinder} instance for fields, and applies it.
|
||||||
|
*
|
||||||
|
* @param output
|
||||||
|
* the collection to which matching fields will be added
|
||||||
|
* @param target
|
||||||
|
* the class to examine
|
||||||
|
* @param fieldFilter
|
||||||
|
* the field filtering function (may be <code>null</code>)
|
||||||
|
* @param classFilter
|
||||||
|
* the class filtering function (may be <code>null</code>)
|
||||||
|
* @return the output collection to which fields matching the specified criteria were added
|
||||||
|
*/
|
||||||
|
public static final Collection< Field > getAllFields( final Collection< Field > output , final Class< ? > target ,
|
||||||
|
final Predicate< Field > fieldFilter , final Predicate< Class< ? > > classFilter )
|
||||||
|
{
|
||||||
|
final MemberFinder< Field > mf = new MemberFinder< >( MemberFinder.FIELD_EXTRACTOR , false );
|
||||||
|
mf.setClassFilter( classFilter );
|
||||||
|
mf.setMemberFilter( fieldFilter );
|
||||||
|
mf.find( output , target );
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Function to use in order to extract the class' members */
|
||||||
|
protected final Function< Class< ? > , T[] > extractor;
|
||||||
|
/** Filter function for classes */
|
||||||
|
protected Predicate< Class< ? > > classFilter;
|
||||||
|
/** Filter function for members */
|
||||||
|
protected Predicate< T > memberFilter;
|
||||||
|
/** <code>true</code> if the finder will go through interfaces, <code>false</code> if it will ignore them */
|
||||||
|
protected final boolean interfaces;
|
||||||
|
/**
|
||||||
|
* If the finder has to go through interfaces, this flag indicates whether they will be examined before or after a
|
||||||
|
* class' superclass. It has no effect if {@link #interfaces} is set to <code>false</code>.
|
||||||
|
*/
|
||||||
|
protected boolean classesFirst;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the member finder by setting the appropriate extractor and whether or not it will go through
|
||||||
|
* interfaces.
|
||||||
|
* <p>
|
||||||
|
* By default, no class or member filtering functions are set, and superclasses will be examined before implemented
|
||||||
|
* interfaces.
|
||||||
|
*
|
||||||
|
* @param extractor
|
||||||
|
* the function to use in order to extract the class' members
|
||||||
|
* @param interfaces
|
||||||
|
* <code>true</code> if the finder must go through interfaces, <code>false</code> if it must ignore them
|
||||||
|
*/
|
||||||
|
public MemberFinder( final Function< Class< ? > , T[] > extractor , final boolean interfaces )
|
||||||
|
{
|
||||||
|
this.extractor = extractor;
|
||||||
|
this.interfaces = interfaces;
|
||||||
|
this.classesFirst = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the member extraction function
|
||||||
|
*
|
||||||
|
* @return the function that extracts class members
|
||||||
|
*/
|
||||||
|
public Function< Class< ? > , T[] > getExtractor( )
|
||||||
|
{
|
||||||
|
return this.extractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the interface-checking flag
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the finder will go through interfaces, <code>false</code> if it will ignore them
|
||||||
|
*/
|
||||||
|
public boolean willCheckInterfaces( )
|
||||||
|
{
|
||||||
|
return this.interfaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the class filtering function
|
||||||
|
*
|
||||||
|
* @return the class filtering function, or <code>null</code> if no class filtering has been configured
|
||||||
|
*/
|
||||||
|
public Predicate< Class< ? > > getClassFilter( )
|
||||||
|
{
|
||||||
|
return this.classFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the class filtering function
|
||||||
|
*
|
||||||
|
* @param classFilter
|
||||||
|
* the new class filtering function, or <code>null</code> to disable class filtering
|
||||||
|
*/
|
||||||
|
public void setClassFilter( final Predicate< Class< ? > > classFilter )
|
||||||
|
{
|
||||||
|
this.classFilter = classFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the member filtering function
|
||||||
|
*
|
||||||
|
* @return the member filtering function
|
||||||
|
*/
|
||||||
|
public Predicate< T > getMemberFilter( )
|
||||||
|
{
|
||||||
|
return this.memberFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the member filtering function
|
||||||
|
*
|
||||||
|
* @param memberFilter
|
||||||
|
* the member filtering function
|
||||||
|
*/
|
||||||
|
public void setMemberFilter( final Predicate< T > memberFilter )
|
||||||
|
{
|
||||||
|
this.memberFilter = memberFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether superclasses are to be examined before or after interfaces.
|
||||||
|
* <p>
|
||||||
|
* Note: meaningless if {@link #willCheckInterfaces()} is <code>false</code>.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if superclasses are examined before interfaces, <code>false</code> if interfaces are
|
||||||
|
* examined before superclasses.
|
||||||
|
*/
|
||||||
|
public boolean willCheckClassesFirst( )
|
||||||
|
{
|
||||||
|
return this.classesFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects whether superclasses are to be examined before or after interfaces.
|
||||||
|
* <p>
|
||||||
|
* Note: meaningless if {@link #willCheckInterfaces()} is <code>false</code>.
|
||||||
|
*
|
||||||
|
* @param classesFirst
|
||||||
|
* <code>true</code> if superclasses are to be examined before interfaces, <code>false</code> if
|
||||||
|
* interfaces are to be examined before superclasses.
|
||||||
|
*/
|
||||||
|
public void setClassesFirst( final boolean classesFirst )
|
||||||
|
{
|
||||||
|
this.classesFirst = classesFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds members matching the currently configured criteria.
|
||||||
|
*
|
||||||
|
* @param output
|
||||||
|
* the collection to which the selected members will be added
|
||||||
|
* @param target
|
||||||
|
* the class to examine
|
||||||
|
* @return the collection to which the selected members were added
|
||||||
|
*/
|
||||||
|
public Collection< T > find( final Collection< T > output , final Class< ? > target )
|
||||||
|
{
|
||||||
|
// We only need to make sure we're not going through the same class twice if we have to go through interfaces
|
||||||
|
final HashSet< Class< ? > > checked;
|
||||||
|
if ( this.interfaces ) {
|
||||||
|
checked = new HashSet< >( );
|
||||||
|
} else {
|
||||||
|
checked = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.findInternal( output , checked , target );
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method that implements the actual look-up.
|
||||||
|
*
|
||||||
|
* @param output
|
||||||
|
* the collection to which the selected members will be added
|
||||||
|
* @param checked
|
||||||
|
* the set of classes that were already examined; will be <code>null</code> if {@link #interfaces} is set
|
||||||
|
* to <code>false</code>.
|
||||||
|
* @param target
|
||||||
|
* the class being examined
|
||||||
|
*/
|
||||||
|
protected void findInternal( final Collection< T > output , final HashSet< Class< ? > > checked ,
|
||||||
|
final Class< ? > target )
|
||||||
|
{
|
||||||
|
if ( target == null || this.classFilter != null && !this.classFilter.test( target ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checked.add( target );
|
||||||
|
|
||||||
|
if ( this.interfaces && !this.classesFirst ) {
|
||||||
|
for ( final Class< ? > intf : target.getInterfaces( ) ) {
|
||||||
|
if ( !checked.contains( intf ) ) {
|
||||||
|
this.findInternal( output , checked , intf );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.findInternal( output , checked , target.getSuperclass( ) );
|
||||||
|
|
||||||
|
if ( this.interfaces && this.classesFirst ) {
|
||||||
|
this.checkInterfaces( output , checked , target );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method that checks a class' interfaces recursively
|
||||||
|
*
|
||||||
|
* @param output
|
||||||
|
* the collection to which the selected members will be added
|
||||||
|
* @param checked
|
||||||
|
* the set of classes that were already examined
|
||||||
|
* @param target
|
||||||
|
* the class being examined
|
||||||
|
*/
|
||||||
|
private void checkInterfaces( final Collection< T > output , final HashSet< Class< ? > > checked ,
|
||||||
|
final Class< ? > target )
|
||||||
|
{
|
||||||
|
for ( final Class< ? > intf : target.getInterfaces( ) ) {
|
||||||
|
if ( !checked.contains( intf ) ) {
|
||||||
|
this.findInternal( output , checked , intf );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue