ConfigurationBuilder.java
package org.reflections.util;
import org.reflections.Configuration;
import org.reflections.ReflectionsException;
import org.reflections.scanners.Scanner;
import org.reflections.scanners.Scanners;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* {@link Configuration} builder for instantiating Reflections
* <pre>{@code
* // add urls for package prefix, use default scanners
* new Reflections(
* new ConfigurationBuilder()
* .forPackage("org.reflections"))
*
* new Reflections(
* new ConfigurationBuilder()
* .addUrls(ClasspathHelper.forPackage("org.reflections")) // add urls for package prefix
* .addScanners(Scanners.values()) // use all standard scanners
* .filterInputsBy(new FilterBuilder().includePackage(...))) // optionally filter inputs
* }</pre>
* <p>defaults scanners: {@link Scanners#SubTypes} and {@link Scanners#TypesAnnotated}
* <p><i>(breaking changes) Inputs filter will NOT be set automatically, consider adding in case too many classes are scanned.</i>
*/
public class ConfigurationBuilder implements Configuration {
public static final Set<Scanner> DEFAULT_SCANNERS = new HashSet<>(Arrays.asList(Scanners.TypesAnnotated, Scanners.SubTypes));
public static final Predicate<String> DEFAULT_INPUTS_FILTER = t -> true;
private Set<Scanner> scanners;
private Set<URL> urls;
private Predicate<String> inputsFilter;
private boolean isParallel = true;
private ClassLoader[] classLoaders;
private boolean expandSuperTypes = true;
public ConfigurationBuilder() {
urls = new HashSet<>();
}
/** constructs a {@link ConfigurationBuilder}.
* <p>each parameter in {@code params} is referred by its type:
* <ul>
* <li>{@link String} - add urls using {@link ClasspathHelper#forPackage(String, ClassLoader...)} and an input filter
* <li>{@link Class} - add urls using {@link ClasspathHelper#forClass(Class, ClassLoader...)} and an input filter
* <li>{@link Scanner} - use scanner, overriding default scanners
* <li>{@link URL} - add url for scanning
* <li>{@link Predicate} - set/override inputs filter
* <li>{@link ClassLoader} - use these classloaders in order to find urls using ClasspathHelper and for resolving types
* <li>{@code Object[]} - flatten and use each element as above
* </ul>
* input filter will be set according to given packages
* <p></p><i>prefer using the explicit accessor methods instead:</i>
* <pre>{@code new ConfigurationBuilder().forPackage(...).setScanners(...)}</pre>
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static ConfigurationBuilder build(Object... params) {
final ConfigurationBuilder builder = new ConfigurationBuilder();
// flatten
List<Object> parameters = new ArrayList<>();
for (Object param : params) {
if (param != null) {
if (param.getClass().isArray()) { for (Object p : (Object[]) param) parameters.add(p); }
else if (param instanceof Iterable) { for (Object p : (Iterable) param) parameters.add(p); }
else parameters.add(param);
}
}
ClassLoader[] loaders = Stream.of(params).filter(p -> p instanceof ClassLoader).distinct().toArray(ClassLoader[]::new);
if (loaders.length != 0) { builder.addClassLoaders(loaders); }
FilterBuilder inputsFilter = new FilterBuilder();
builder.filterInputsBy(inputsFilter);
for (Object param : parameters) {
if (param instanceof String && !((String) param).isEmpty()) {
builder.forPackage((String) param, loaders);
inputsFilter.includePackage((String) param);
} else if (param instanceof Class && !Scanner.class.isAssignableFrom((Class) param)) {
builder.addUrls(ClasspathHelper.forClass((Class) param, loaders));
inputsFilter.includePackage(((Class) param).getPackage().getName());
} else if (param instanceof URL) {
builder.addUrls((URL) param);
} else if (param instanceof Scanner) {
builder.addScanners((Scanner) param);
} else if (param instanceof Class && Scanner.class.isAssignableFrom((Class) param)) {
try { builder.addScanners(((Class<Scanner>) param).getDeclaredConstructor().newInstance()); }
catch (Exception e) { throw new RuntimeException(e); }
} else if (param instanceof Predicate) {
builder.filterInputsBy((Predicate<String>) param);
} else throw new ReflectionsException("could not use param '" + param + "'");
}
if (builder.getUrls().isEmpty()) {
// scan all classpath if no urls provided todo avoid
builder.addUrls(ClasspathHelper.forClassLoader(loaders));
}
return builder;
}
/** {@link #addUrls(URL...)} by applying {@link ClasspathHelper#forPackage(String, ClassLoader...)} for the given {@code pkg}*/
public ConfigurationBuilder forPackage(String pkg, ClassLoader... classLoaders) {
return addUrls(ClasspathHelper.forPackage(pkg, classLoaders));
}
/** {@link #addUrls(URL...)} by applying {@link ClasspathHelper#forPackage(String, ClassLoader...)} for the given {@code packages}*/
public ConfigurationBuilder forPackages(String... packages) {
for (String pkg : packages) forPackage(pkg);
return this;
}
@Override
/* @inherited */
public Set<Scanner> getScanners() {
return scanners != null ? scanners : DEFAULT_SCANNERS;
}
/** set the scanners instances for scanning different metadata */
public ConfigurationBuilder setScanners(Scanner... scanners) {
this.scanners = new HashSet<>(Arrays.asList(scanners));
return this;
}
/** set the scanners instances for scanning different metadata */
public ConfigurationBuilder addScanners(Scanner... scanners) {
if (this.scanners == null) setScanners(scanners); else this.scanners.addAll(Arrays.asList(scanners));
return this;
}
@Override
/* @inherited */
public Set<URL> getUrls() {
return urls;
}
/** set the urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder setUrls(Collection<URL> urls) {
this.urls = new HashSet<>(urls);
return this;
}
/** set the urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder setUrls(URL... urls) {
return setUrls(Arrays.asList(urls));
}
/** add urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder addUrls(Collection<URL> urls) {
this.urls.addAll(urls);
return this;
}
/** add urls to be scanned
* <p>use {@link ClasspathHelper} convenient methods to get the relevant urls
* <p>see also {@link #forPackages(String...)} */
public ConfigurationBuilder addUrls(URL... urls) {
return addUrls(Arrays.asList(urls));
}
@Override
/* @inherited */
public Predicate<String> getInputsFilter() {
return inputsFilter != null ? inputsFilter : DEFAULT_INPUTS_FILTER;
}
/** sets the input filter for all resources to be scanned.
* <p>prefer using {@link FilterBuilder} */
public ConfigurationBuilder setInputsFilter(Predicate<String> inputsFilter) {
this.inputsFilter = inputsFilter;
return this;
}
/** sets the input filter for all resources to be scanned.
* <p>prefer using {@link FilterBuilder} */
public ConfigurationBuilder filterInputsBy(Predicate<String> inputsFilter) {
return setInputsFilter(inputsFilter);
}
@Override
/* @inherited */
public boolean isParallel() {
return isParallel;
}
/** if true, scan urls in parallel. */
public ConfigurationBuilder setParallel(boolean parallel) {
isParallel = parallel;
return this;
}
@Override
/* @inherited */
public ClassLoader[] getClassLoaders() {
return classLoaders;
}
/** set optional class loaders used for resolving types. */
public ConfigurationBuilder setClassLoaders(ClassLoader[] classLoaders) {
this.classLoaders = classLoaders;
return this;
}
/** add optional class loaders used for resolving types. */
public ConfigurationBuilder addClassLoaders(ClassLoader... classLoaders) {
this.classLoaders = this.classLoaders == null ? classLoaders :
Stream.concat(Arrays.stream(this.classLoaders), Arrays.stream(classLoaders)).distinct().toArray(ClassLoader[]::new);
return this;
}
@Override
/* @inherited */
public boolean shouldExpandSuperTypes() {
return expandSuperTypes;
}
/** if set to true, Reflections will expand super types after scanning.
* <p>see {@link org.reflections.Reflections#expandSuperTypes(Map, Map)} */
public ConfigurationBuilder setExpandSuperTypes(boolean expandSuperTypes) {
this.expandSuperTypes = expandSuperTypes;
return this;
}
}