Querying classes with arbitrary properties in Java classpath using the Reflections library

428 views Asked by At

I have used in the past the Reflections library to query classes in my classpath having a particular annotation, or inheriting from a particular class. This is very easy to do with Reflections since the library already provide methods that answer such specific queries.

For example, the code below illustrates how to query classes inheriting from SomeType and classes annotated with SomeAnnotation, looking for classes located in the package my.package.prefix (example taken from the documentation of the Reflection.java class):

Reflections reflections = new Reflections("my.package.prefix");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);

However, in my current problem what I need to do is querying for classes having an arbitrary set of properties (e.g., a combination of a particular class name + existing annotations + whatever ). I do not know these properties in advance since they should be defined by the programmer using the library I am developing.

I envision that part of the solution will be something like asking the programmer to define a Strategy class with a boolean method answering if the class should be in the answer set or not.

For example, the programmer should implement an interface similar to:

public interface MyStrategy {
    public boolean match(Class clazz);
}

So my question is, once I have this strategy object, how I can instruct the Reflections library to filter classes keeping only those that match according to the strategy class defined by the programmer ?

UPDATE:

Apparently the filtering part is not the most difficult task (thanks for the answer @MvG) but actually gathering all the classes to be filtered (typically classes in a set of base packages defined by the programmer). So the idea is querying all the classes and then filtering them with the method: ReflectionUtils.getAll(allClasses, predicate).

Then, in order to obtain the initial set of classes to be filtered, I tried with querying all the subtypes of Object in a particular package chosen by the programmer.

Below the code I used for this:

ConfigurationBuilder config = new ConfigurationBuilder();
FilterBuilder fb = new FilterBuilder();
fb.include(FilterBuilder.prefix("my.package.prefix"));
config.filterInputsBy(fb);
config.setUrls(ClasspathHelper.forPackage("my.package.prefix"));
config.setScanners(new SubTypesScanner(false)); //the first argument at the constructor of SubTypesScanner indicates if the Object class should be ignored
Reflections reflections = new Reflections(config);
Set<Class<? extends Object>> unfilteredClasses = reflections.getSubTypesOf(Object.class);

Is this the best (more efficient) solution ?

Does someone have any alternative idea ?. Thanks in advance !

1

There are 1 answers

0
MvG On BEST ANSWER

Use com.google.common.base.Predicate to express your strategies, or wrap your own strategy objects into such predicates. Then you can write

ReflectionUtils.getAll(allClasses, predicate);

It seems that actually computing this allClasses iterable is going to be the more demanding task. I see no simple API for that at first glance. You'll probably have to access the underlying store directly, and turn class names to Class objects yourself. That means loading all the classes, which might be quite time-consuming, and might even fail in certain scenarios.