getInputsFilter();
- /** executor service used to scan files. if null, scanning is done in a simple for loop */
- ExecutorService getExecutorService();
-
- /** the default serializer to use when saving Reflection */
- Serializer getSerializer();
+ /** scan urls in parallel. defaults to true. */
+ boolean isParallel();
- /** get class loaders, might be used for resolving methods/fields */
+ /** optional class loaders used for resolving types. */
ClassLoader[] getClassLoaders();
/** if true (default), expand super types after scanning, for super types that were not scanned.
- * see {@link org.reflections.Reflections#expandSuperTypes()}*/
+ *
see {@link org.reflections.Reflections#expandSuperTypes(Map)}*/
boolean shouldExpandSuperTypes();
}
diff --git a/src/main/java/org/reflections/ReflectionUtils.java b/src/main/java/org/reflections/ReflectionUtils.java
index fe1b8322..ee052f15 100644
--- a/src/main/java/org/reflections/ReflectionUtils.java
+++ b/src/main/java/org/reflections/ReflectionUtils.java
@@ -1,403 +1,259 @@
package org.reflections;
-import org.reflections.util.ClasspathHelper;
+import org.reflections.util.QueryFunction;
+import org.reflections.util.ReflectionUtilsPredicates;
+import org.reflections.util.UtilQueryBuilder;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
-import java.lang.reflect.Member;
import java.lang.reflect.Method;
-import java.util.ArrayList;
+import java.lang.reflect.Proxy;
import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Objects;
+import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-import static org.reflections.util.Utils.filter;
-
-/** convenient java reflection helper methods
- *
- * 1. some helper methods to get type by name: {@link #forName(String, ClassLoader...)} and {@link #forNames(Collection, ClassLoader...)} )}
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.toList;
+
+/**
+ * utils for querying java reflection meta types {@link #SuperTypes}, {@link #Annotations}, {@link #AnnotationTypes}, {@link #Methods}, {@link #Constructors}, {@link #Fields}.
+ *
{@code Set> supertypes = get(SuperTypes.of(type))
+ * Set annotations = get(Annotations.of(type))
+ * }
+ * generally, apply {@link #get(QueryFunction)} on {@link QueryFunction} created by {@link UtilQueryBuilder}, and optionally use the functional methods in QueryFunction
+ *
{@code get(Methods.of(type)
+ * .filter(withPublic().and(withPrefix("get")).and(withParameterCount(0)))
+ * .as(Method.class)
+ * .map(m -> ...))
+ * }
+ * or (previously), use {@code getAllXXX(type/s, withYYY)} methods:
+ *
{@code getAllSuperTypes(), getAllFields(), getAllMethods(), getAllConstructors() }
+ *
*
- * 2. some helper methods to get all types/methods/fields/constructors/properties matching some predicates, generally:
- *
Set<?> result = getAllXXX(type/s, withYYY)
- * where get methods are:
- *
- * - {@link #getAllSuperTypes(Class, java.util.function.Predicate...)}
- *
- {@link #getAllFields(Class, java.util.function.Predicate...)}
- *
- {@link #getAllMethods(Class, java.util.function.Predicate...)}
- *
- {@link #getAllConstructors(Class, java.util.function.Predicate...)}
- *
- * and predicates included here all starts with "with", such as
- *
- * - {@link #withAnnotation(java.lang.annotation.Annotation)}
- *
- {@link #withModifier(int)}
- *
- {@link #withName(String)}
- *
- {@link #withParameters(Class[])}
- *
- {@link #withAnyParameterAnnotation(Class)}
- *
- {@link #withParametersAssignableTo(Class[])}
- *
- {@link #withParametersAssignableFrom(Class[])}
- *
- {@link #withPrefix(String)}
- *
- {@link #withReturnType(Class)}
- *
- {@link #withType(Class)}
- *
- {@link #withTypeAssignableTo}
- *
+ * some predicates included here:
+ *
+ * - {@link #withPublic()}
+ *
- {@link #withParametersCount(int)}}
+ *
- {@link #withAnnotation(java.lang.annotation.Annotation)}
+ *
- {@link #withParameters(Class[])}
+ *
- {@link #withModifier(int)}
+ *
- {@link #withReturnType(Class)}
+ *
+ * {@code
+ * import static org.reflections.ReflectionUtils.*;
*
- *
- * for example, getting all getters would be:
- *
- * Set<Method> getters = getAllMethods(someClasses,
- * Predicates.and(
- * withModifier(Modifier.PUBLIC),
- * withPrefix("get"),
- * withParametersCount(0)));
- *
+ * Set getters =
+ * get(Methods(classes)
+ * .filter(withModifier(Modifier.PUBLIC).and(withPrefix("get")).and(withParametersCount(0)));
+ *
+ * get(Annotations.of(method)
+ * .filter(withAnnotation())
+ * .map(annotation -> Methods.of(annotation)
+ * .map(method -> )))))
+ * .stream()...
+ * }
* */
-@SuppressWarnings("unchecked")
-public abstract class ReflectionUtils {
-
- /** would include {@code Object.class} when {@link #getAllSuperTypes(Class, java.util.function.Predicate[])}. default is false. */
- public static boolean includeObject = false;
+@SuppressWarnings({"unchecked", "rawtypes"})
+public abstract class ReflectionUtils extends ReflectionUtilsPredicates {
- /** get all super types of given {@code type}, including, optionally filtered by {@code predicates}
- * include {@code Object.class} if {@link #includeObject} is true */
- public static Set> getAllSuperTypes(final Class> type, Predicate super Class>>... predicates) {
- Set> result = new LinkedHashSet<>();
- if (type != null && (includeObject || !type.equals(Object.class))) {
- result.add(type);
- for (Class> supertype : getSuperTypes(type)) {
- result.addAll(getAllSuperTypes(supertype));
- }
- }
- return filter(result, predicates);
+ /** get type elements {@code } by applying {@link QueryFunction} {@code get(SuperTypes.of(type))}
*/
+ public static Set get(QueryFunction function) {
+ return function.apply(null);
}
- /** get the immediate supertype and interfaces of the given {@code type} */
- public static Set> getSuperTypes(Class> type) {
- Set> result = new LinkedHashSet<>();
- Class> superclass = type.getSuperclass();
- Class>[] interfaces = type.getInterfaces();
- if (superclass != null && (includeObject || !superclass.equals(Object.class))) result.add(superclass);
- if (interfaces != null && interfaces.length > 0) result.addAll(Arrays.asList(interfaces));
- return result;
+ /** get type elements {@code } by applying {@link QueryFunction} and {@code predicates} */
+ public static Set get(QueryFunction queryFunction, Predicate super T>... predicates) {
+ return get(queryFunction.filter(Arrays.stream((Predicate[]) predicates).reduce(t -> true, Predicate::and)));
}
- /** get all methods of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */
- public static Set getAllMethods(final Class> type, Predicate super Method>... predicates) {
- Set result = new HashSet<>();
- for (Class> t : getAllSuperTypes(type)) {
- result.addAll(getMethods(t, predicates));
- }
- return result;
- }
-
- /** get methods of given {@code type}, optionally filtered by {@code predicates} */
- public static Set getMethods(Class> t, Predicate super Method>... predicates) {
- return filter(t.isInterface() ? t.getMethods() : t.getDeclaredMethods(), predicates);
- }
-
- /** get all constructors of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */
- public static Set getAllConstructors(final Class> type, Predicate super Constructor>... predicates) {
- Set result = new HashSet<>();
- for (Class> t : getAllSuperTypes(type)) {
- result.addAll(getConstructors(t, predicates));
- }
- return result;
- }
+ private static final List objectMethodNames =
+ Arrays.asList("equals", "hashCode", "toString", "wait", "notify", "notifyAll");
- /** get constructors of given {@code type}, optionally filtered by {@code predicates} */
- public static Set getConstructors(Class> t, Predicate super Constructor>... predicates) {
- return filter(t.getDeclaredConstructors(), predicates);
- }
+ /** predicate to filter out {@code Object} methods */
+ public static final Predicate notObjectMethod = m -> !objectMethodNames.contains(m.getName());
- /** get all fields of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */
- public static Set getAllFields(final Class> type, Predicate super Field>... predicates) {
- Set result = new HashSet<>();
- for (Class> t : getAllSuperTypes(type)) result.addAll(getFields(t, predicates));
- return result;
- }
+ /** query super class {@code get(SuperClass.of(element)) -> Set>}
+ * see also {@link ReflectionUtils#SuperTypes}, {@link ReflectionUtils#Interfaces} */
+ public static final UtilQueryBuilder, Class>> SuperClass =
+ element -> ctx -> {
+ Class> superclass = element.getSuperclass();
+ return superclass != null && !superclass.equals(Object.class) ? Collections.singleton(superclass) : Collections.emptySet();
+ };
- /** get fields of given {@code type}, optionally filtered by {@code predicates} */
- public static Set getFields(Class> type, Predicate super Field>... predicates) {
- return filter(type.getDeclaredFields(), predicates);
- }
+ /** query interfaces {@code get(Interfaces.of(element)) -> Set>}
*/
+ public static final UtilQueryBuilder, Class>> Interfaces =
+ element -> ctx -> Stream.of(element.getInterfaces()).collect(Collectors.toCollection(LinkedHashSet::new));
- /** get all annotations of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */
- public static Set getAllAnnotations(T type, Predicate... predicates) {
- Set result = new LinkedHashSet<>();
- List keys = new ArrayList();
- if (type instanceof Class) {
- keys.addAll(getAllSuperTypes((Class>) type));
- }
- for (int i = 0; i < keys.size(); i++) {
- for (Annotation annotation : getAnnotations(keys.get(i), predicates)) {
- if (result.add(annotation)) {
- keys.add(annotation.annotationType());
- }
+ /** query super classes and interfaces including element {@code get(SuperTypes.of(element)) -> Set> }
*/
+ public static final UtilQueryBuilder, Class>> SuperTypes =
+ new UtilQueryBuilder, Class>>() {
+ @Override
+ public QueryFunction> get(Class> element) {
+ return SuperClass.get(element).add(Interfaces.get(element));
}
- }
- return result;
- }
-
- /** get annotations of given {@code type}, optionally honorInherited, optionally filtered by {@code predicates} */
- public static Set getAnnotations(T type, Predicate... predicates) {
- return filter(type.getDeclaredAnnotations(), predicates);
- }
-
- /** filter all given {@code elements} with {@code predicates}, if given */
- public static Set getAll(final Set elements, Predicate super T>... predicates) {
- return filter(elements, predicates);
- }
-
- //predicates
- /** where member name equals given {@code name} */
- public static Predicate withName(final String name) {
- return input -> input != null && input.getName().equals(name);
- }
-
- /** where member name startsWith given {@code prefix} */
- public static Predicate withPrefix(final String prefix) {
- return input -> input != null && input.getName().startsWith(prefix);
- }
-
- /** where member's {@code toString} matches given {@code regex}
- * for example:
- *
- * getAllMethods(someClass, withPattern("public void .*"))
- *
- * */
- public static Predicate withPattern(final String regex) {
- return input -> Pattern.matches(regex, input.toString());
- }
-
- /** where element is annotated with given {@code annotation} */
- public static Predicate withAnnotation(final Class extends Annotation> annotation) {
- return input -> input != null && input.isAnnotationPresent(annotation);
- }
- /** where element is annotated with given {@code annotations} */
- public static Predicate withAnnotations(final Class extends Annotation>... annotations) {
- return input -> input != null && Arrays.equals(annotations, annotationTypes(input.getAnnotations()));
- }
+ @Override
+ public QueryFunction> of(Class> element) {
+ return QueryFunction.>single(element).getAll(SuperTypes::get);
+ }
+ };
- /** where element is annotated with given {@code annotation}, including member matching */
- public static Predicate withAnnotation(final Annotation annotation) {
- return input -> input != null && input.isAnnotationPresent(annotation.annotationType()) &&
- areAnnotationMembersMatching(input.getAnnotation(annotation.annotationType()), annotation);
- }
+ /** query annotations {@code get(Annotation.of(element)) -> Set }
*/
+ public static final UtilQueryBuilder Annotations =
+ new UtilQueryBuilder() {
+ @Override
+ public QueryFunction get(AnnotatedElement element) {
+ return ctx -> Arrays.stream(element.getAnnotations()).collect(Collectors.toCollection(LinkedHashSet::new));
+ }
- /** where element is annotated with given {@code annotations}, including member matching */
- public static Predicate withAnnotations(final Annotation... annotations) {
- return input -> {
- if (input != null) {
- Annotation[] inputAnnotations = input.getAnnotations();
- if (inputAnnotations.length == annotations.length) {
- return IntStream.range(0, inputAnnotations.length)
- .allMatch(i -> areAnnotationMembersMatching(inputAnnotations[i], annotations[i]));
- }
+ @Override
+ public QueryFunction of(AnnotatedElement element) {
+ return ReflectionUtils.extendType().get(element).getAll(Annotations::get, Annotation::annotationType);
}
- return true;
};
- }
- /** when method/constructor parameter types equals given {@code types} */
- public static Predicate withParameters(final Class>... types) {
- return input -> Arrays.equals(parameterTypes(input), types);
- }
+ /** query annotation types {@code get(AnnotationTypes.of(element)) -> Set> }
*/
+ public static final UtilQueryBuilder> AnnotationTypes =
+ new UtilQueryBuilder>() {
+ @Override
+ public QueryFunction> get(AnnotatedElement element) {
+ return Annotations.get(element).map(Annotation::annotationType);
+ }
- /** when member parameter types assignable to given {@code types} */
- public static Predicate withParametersAssignableTo(final Class... types) {
- return input -> isAssignable(types, parameterTypes(input));
- }
+ @Override
+ public QueryFunction> of(AnnotatedElement element) {
+ return ReflectionUtils.extendType().get(element).getAll(AnnotationTypes::get, a -> a);
+ }
+ };
- /** when method/constructor parameter types assignable from given {@code types} */
- public static Predicate withParametersAssignableFrom(final Class... types) {
- return input -> isAssignable(parameterTypes(input), types);
- }
+ /** query methods {@code get(Methods.of(type)) -> Set}
*/
+ public static final UtilQueryBuilder, Method> Methods =
+ element -> ctx -> Arrays.stream(element.getMethods()).filter(notObjectMethod).collect(Collectors.toCollection(LinkedHashSet::new));
- /** when method/constructor parameters count equal given {@code count} */
- public static Predicate withParametersCount(final int count) {
- return input -> input != null && parameterTypes(input).length == count;
- }
+ /** query constructors {@code get(Constructors.of(type)) -> Set }
*/
+ public static final UtilQueryBuilder, Constructor> Constructors =
+ element -> ctx -> Arrays.stream(element.getDeclaredConstructors()).collect(Collectors.toCollection(LinkedHashSet::new));
- /** when method/constructor has any parameter with an annotation matches given {@code annotations} */
- public static Predicate withAnyParameterAnnotation(final Class extends Annotation> annotationClass) {
- return input -> input != null && annotationTypes(parameterAnnotations(input)).stream().anyMatch(input1 -> input1.equals(annotationClass));
- }
+ /** query fields {@code get(Fields.of(type)) -> Set }
*/
+ public static final UtilQueryBuilder, Field> Fields =
+ element -> ctx -> Arrays.stream(element.getDeclaredFields()).collect(Collectors.toCollection(LinkedHashSet::new));
- /** when method/constructor has any parameter with an annotation matches given {@code annotations}, including member matching */
- public static Predicate withAnyParameterAnnotation(final Annotation annotation) {
- return input -> input != null && parameterAnnotations(input).stream().anyMatch(input1 -> areAnnotationMembersMatching(annotation, input1));
+ public static UtilQueryBuilder extendType() {
+ return element -> {
+ if (element instanceof Class && !((Class>) element).isAnnotation()) {
+ QueryFunction> single = QueryFunction.single((Class>) element);
+ return (QueryFunction) single.add(single.getAll(SuperTypes::get));
+ } else {
+ return QueryFunction.single((T) element);
+ }
+ };
}
- /** when field type equal given {@code type} */
- public static Predicate withType(final Class type) {
- return input -> input != null && input.getType().equals(type);
+ /** get all annotations of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Annotations.of())} */
+ public static Set getAllAnnotations(T type, Predicate... predicates) {
+ return get(Annotations.of(type), predicates);
}
- /** when field type assignable to given {@code type} */
- public static Predicate withTypeAssignableTo(final Class type) {
- return input -> input != null && type.isAssignableFrom(input.getType());
+ /** get all super types of given {@code type}, including, optionally filtered by {@code predicates} */
+ public static Set> getAllSuperTypes(final Class> type, Predicate super Class>>... predicates) {
+ Predicate super Class>>[] filter = predicates == null || predicates.length == 0 ? new Predicate[]{t -> !Object.class.equals(t)} : predicates;
+ return get(SuperTypes.of(type), filter);
}
- /** when method return type equal given {@code type} */
- public static Predicate withReturnType(final Class type) {
- return input -> input != null && input.getReturnType().equals(type);
+ /** get the immediate supertype and interfaces of the given {@code type}
+ * marked for removal, use instead {@code get(SuperTypes.get())} */
+ public static Set> getSuperTypes(Class> type) {
+ return get(SuperTypes.get(type));
}
- /** when method return type assignable from given {@code type} */
- public static Predicate withReturnTypeAssignableTo(final Class type) {
- return input -> input != null && type.isAssignableFrom(input.getReturnType());
+ /** get all methods of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Methods.of())} */
+ public static Set getAllMethods(final Class> type, Predicate super Method>... predicates) {
+ return get(Methods.of(type), predicates);
}
- /** when member modifier matches given {@code mod}
- * for example:
- *
- * withModifier(Modifier.PUBLIC)
- *
- */
- public static Predicate withModifier(final int mod) {
- return input -> input != null && (input.getModifiers() & mod) != 0;
+ /** get methods of given {@code type}, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Methods.get())} */
+ public static Set getMethods(Class> t, Predicate super Method>... predicates) {
+ return get(Methods.get(t), predicates);
}
- /** when class modifier matches given {@code mod}
- * for example:
- *
- * withModifier(Modifier.PUBLIC)
- *
- */
- public static Predicate> withClassModifier(final int mod) {
- return input -> input != null && (input.getModifiers() & mod) != 0;
+ /** get all constructors of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Constructors.of())} */
+ public static Set getAllConstructors(final Class> type, Predicate super Constructor>... predicates) {
+ return get(Constructors.of(type), predicates);
}
- //
- /** tries to resolve a java type name to a Class
- * if optional {@link ClassLoader}s are not specified, then both {@link org.reflections.util.ClasspathHelper#contextClassLoader()} and {@link org.reflections.util.ClasspathHelper#staticClassLoader()} are used
- * */
- public static Class> forName(String typeName, ClassLoader... classLoaders) {
- if (getPrimitiveNames().contains(typeName)) {
- return getPrimitiveTypes().get(getPrimitiveNames().indexOf(typeName));
- } else {
- String type;
- if (typeName.contains("[")) {
- int i = typeName.indexOf("[");
- type = typeName.substring(0, i);
- String array = typeName.substring(i).replace("]", "");
-
- if (getPrimitiveNames().contains(type)) {
- type = getPrimitiveDescriptors().get(getPrimitiveNames().indexOf(type));
- } else {
- type = "L" + type + ";";
- }
-
- type = array + type;
- } else {
- type = typeName;
- }
-
- List reflectionsExceptions = new ArrayList<>();
- for (ClassLoader classLoader : ClasspathHelper.classLoaders(classLoaders)) {
- if (type.contains("[")) {
- try { return Class.forName(type, false, classLoader); }
- catch (Throwable e) {
- reflectionsExceptions.add(new ReflectionsException("could not get type for name " + typeName, e));
- }
- }
- try { return classLoader.loadClass(type); }
- catch (Throwable e) {
- reflectionsExceptions.add(new ReflectionsException("could not get type for name " + typeName, e));
- }
- }
-
- if (Reflections.log != null && Reflections.log.isTraceEnabled()) {
- for (ReflectionsException reflectionsException : reflectionsExceptions) {
- Reflections.log.trace("could not get type for name " + typeName + " from any class loader", reflectionsException);
- }
- }
-
- return null;
- }
+ /** get constructors of given {@code type}, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Constructors.get())} */
+ public static Set getConstructors(Class> t, Predicate super Constructor>... predicates) {
+ return get(Constructors.get(t), predicates);
}
- /** try to resolve all given string representation of types to a list of java types */
- public static Set> forNames(final Collection classes, ClassLoader... classLoaders) {
- return classes.stream()
- .map(className -> (Class extends T>) forName(className, classLoaders))
- .filter(Objects::nonNull)
- .collect(Collectors.toCollection(LinkedHashSet::new));
+ /** get all fields of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Fields.of())} */
+ public static Set getAllFields(final Class> type, Predicate super Field>... predicates) {
+ return get(Fields.of(type), predicates);
}
- private static Class[] parameterTypes(Member member) {
- return member != null ?
- member.getClass() == Method.class ? ((Method) member).getParameterTypes() :
- member.getClass() == Constructor.class ? ((Constructor) member).getParameterTypes() : null : null;
+ /** get fields of given {@code type}, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Fields.get())} */
+ public static Set getFields(Class> type, Predicate super Field>... predicates) {
+ return get(Fields.get(type), predicates);
}
- private static Set parameterAnnotations(Member member) {
- Annotation[][] annotations =
- member instanceof Method ? ((Method) member).getParameterAnnotations() :
- member instanceof Constructor ? ((Constructor) member).getParameterAnnotations() : null;
- return Arrays.stream(annotations).flatMap(Arrays::stream).collect(Collectors.toSet());
+ /** get annotations of given {@code type}, optionally honorInherited, optionally filtered by {@code predicates}
+ * marked for removal, use instead {@code get(Annotations.get())} */
+ public static Set getAnnotations(T type, Predicate... predicates) {
+ return get(Annotations.get(type), predicates);
}
- private static Set> annotationTypes(Collection annotations) {
- return annotations.stream().map(Annotation::annotationType).collect(Collectors.toSet());
+ /** map {@code annotation} to hash map of member values recursively {@code Annotations.of(type).map(ReflectionUtils::toMap)}
*/
+ public static Map toMap(Annotation annotation) {
+ return get(Methods.of(annotation.annotationType())
+ .filter(notObjectMethod.and(withParametersCount(0))))
+ .stream()
+ .collect(Collectors.toMap(Method::getName, m -> {
+ Object v1 = invoke(m, annotation);
+ return v1.getClass().isArray() && v1.getClass().getComponentType().isAnnotation() ?
+ Stream.of((Annotation[]) v1).map(ReflectionUtils::toMap).collect(toList()) : v1;
+ }));
}
- private static Class extends Annotation>[] annotationTypes(Annotation[] annotations) {
- return Arrays.stream(annotations).map(Annotation::annotationType).toArray(Class[]::new);
+ /** map {@code annotation} and {@code annotatedElement} to hash map of member values
+ * {@code Annotations.of(type).map(a -> toMap(type, a))}
*/
+ public static Map toMap(Annotation annotation, AnnotatedElement element) {
+ Map map = toMap(annotation);
+ if (element != null) map.put("annotatedElement", element);
+ return map;
}
- //
- private static List primitiveNames;
- private static List primitiveTypes;
- private static List primitiveDescriptors;
-
- private static void initPrimitives() {
- if (primitiveNames == null) {
- primitiveNames = Arrays.asList("boolean", "char", "byte", "short", "int", "long", "float", "double", "void");
- primitiveTypes = Arrays.asList(boolean.class, char.class, byte.class, short.class, int.class, long.class, float.class, double.class, void.class);
- primitiveDescriptors = Arrays.asList("Z", "C", "B", "S", "I", "J", "F", "D", "V");
- }
+ /** create new annotation proxy with member values from the given {@code map} {@code toAnnotation(Map.of("annotationType", annotationType, "value", ""))}
*/
+ public static Annotation toAnnotation(Map map) {
+ return toAnnotation(map, (Class extends Annotation>) map.get("annotationType"));
}
- private static List getPrimitiveNames() { initPrimitives(); return primitiveNames; }
- private static List getPrimitiveTypes() { initPrimitives(); return primitiveTypes; }
- private static List getPrimitiveDescriptors() { initPrimitives(); return primitiveDescriptors; }
-
- //
- private static boolean areAnnotationMembersMatching(Annotation annotation1, Annotation annotation2) {
- if (annotation2 != null && annotation1.annotationType() == annotation2.annotationType()) {
- for (Method method : annotation1.annotationType().getDeclaredMethods()) {
- try {
- if (!method.invoke(annotation1).equals(method.invoke(annotation2))) return false;
- } catch (Exception e) {
- throw new ReflectionsException(String.format("could not invoke method %s on annotation %s", method.getName(), annotation1.annotationType()), e);
- }
- }
- return true;
- }
- return false;
+ /** create new annotation proxy with member values from the given {@code map} and member values from the given {@code map}
+ * {@code toAnnotation(Map.of("value", ""), annotationType)}
*/
+ public static T toAnnotation(Map map, Class annotationType) {
+ return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class>[]{annotationType},
+ (proxy, method, args) -> notObjectMethod.test(method) ? map.get(method.getName()) : method.invoke(map));
}
-
- private static boolean isAssignable(Class[] childClasses, Class[] parentClasses) {
- if (childClasses == null) {
- return parentClasses == null || parentClasses.length == 0;
- }
- if (childClasses.length != parentClasses.length) {
- return false;
+ /** invoke the given {@code method} with {@code args}, return either the result or an exception if occurred */
+ public static Object invoke(Method method, Object obj, Object... args) {
+ try {
+ return method.invoke(obj, args);
+ } catch (Exception e) {
+ return e;
}
- return IntStream.range(0, childClasses.length)
- .noneMatch(i -> !parentClasses[i].isAssignableFrom(childClasses[i]) ||
- parentClasses[i] == Object.class && childClasses[i] != Object.class);
}
}
diff --git a/src/main/java/org/reflections/Reflections.java b/src/main/java/org/reflections/Reflections.java
index 6697ca03..3a297077 100644
--- a/src/main/java/org/reflections/Reflections.java
+++ b/src/main/java/org/reflections/Reflections.java
@@ -1,291 +1,235 @@
package org.reflections;
-import org.reflections.scanners.FieldAnnotationsScanner;
+import javassist.bytecode.ClassFile;
import org.reflections.scanners.MemberUsageScanner;
-import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.MethodParameterNamesScanner;
-import org.reflections.scanners.MethodParameterScanner;
-import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.Scanner;
-import org.reflections.scanners.SubTypesScanner;
-import org.reflections.scanners.TypeAnnotationsScanner;
+import org.reflections.scanners.Scanners;
import org.reflections.serializers.Serializer;
import org.reflections.serializers.XmlSerializer;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
-import org.reflections.util.Utils;
+import org.reflections.util.NameHelper;
+import org.reflections.util.QueryFunction;
import org.reflections.vfs.Vfs;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import javax.annotation.Nullable;
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.annotation.Inherited;
+import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import static java.lang.String.format;
-import static org.reflections.ReflectionUtils.*;
-import static org.reflections.util.Utils.*;
+import static org.reflections.ReflectionUtils.withAnnotation;
+import static org.reflections.ReflectionUtils.withAnyParameterAnnotation;
+import static org.reflections.scanners.Scanners.*;
/**
* Reflections one-stop-shop object
- * Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project.
- *
Using Reflections you can query your metadata such as:
+ *
+ * Reflections scans and indexes your project's classpath, allowing reverse query of the type system metadata on runtime.
+ * Using Reflections you can query for example:
*
- * - get all subtypes of some type
- *
- get all types/constructors/methods/fields annotated with some annotation, optionally with annotation parameters matching
- *
- get all resources matching matching a regular expression
- *
- get all methods with specific signature including parameters, parameter annotations and return type
- *
- get all methods parameter names
- *
- get all fields/methods/constructors usages in code
+ *
- Subtypes of a type
+ *
- Types annotated with an annotation
+ *
- Methods with annotation, parameters, return type
+ *
- Resources found in classpath
+ *
And more...
*
- * A typical use of Reflections would be:
- *
- * Reflections reflections = new Reflections("my.project.prefix");
*
- * Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
+ * Create Reflections instance, preferably using {@link ConfigurationBuilder}:
+ *
{@code Reflections reflections = new Reflections(
+ * new ConfigurationBuilder()
+ * .forPackage("com.my.project"));
*
- * Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);
- *
- * Basically, to use Reflections first instantiate it with one of the constructors, then depending on the scanners, use the convenient query methods:
- *
- * Reflections reflections = new Reflections("my.package.prefix");
- * //or
- * Reflections reflections = new Reflections(ClasspathHelper.forPackage("my.package.prefix"),
- * new SubTypesScanner(), new TypesAnnotationScanner(), new FilterBuilder().include(...), ...);
+ * // or similarly
+ * Reflections reflections = new Reflections("com.my.project");
*
- * //or using the ConfigurationBuilder
- * new Reflections(new ConfigurationBuilder()
- * .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("my.project.prefix")))
- * .setUrls(ClasspathHelper.forPackage("my.project.prefix"))
- * .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner().filterResultsBy(optionalFilter), ...));
- *
- * And then query, for example:
- *
- * Set<Class<? extends Module>> modules = reflections.getSubTypesOf(com.google.inject.Module.class);
- * Set<Class<?>> singletons = reflections.getTypesAnnotatedWith(javax.inject.Singleton.class);
+ * // another example
+ * Reflections reflections = new Reflections(
+ * new ConfigurationBuilder()
+ * .forPackage("com.my.project")
+ * .setScanners(Scanners.values()) // all standard scanners
+ * .filterInputsBy(new FilterBuilder().includePackage("com.my.project").excludePackage("com.my.project.exclude")));
+ * }
*
- * Set<String> properties = reflections.getResources(Pattern.compile(".*\\.properties"));
- * Set<Constructor> injectables = reflections.getConstructorsAnnotatedWith(javax.inject.Inject.class);
- * Set<Method> deprecateds = reflections.getMethodsAnnotatedWith(javax.ws.rs.Path.class);
- * Set<Field> ids = reflections.getFieldsAnnotatedWith(javax.persistence.Id.class);
+ * All relevant URLs should be configured.
+ *
If required, Reflections will {@link #expandSuperTypes(Map)} in order to get the transitive closure metadata without scanning large 3rd party urls.
+ *
{@link Scanners} must be configured in order to be queried, otherwise an empty result is returned.
+ *
Default scanners are {@code SubTypes} and {@code TypesAnnotated}.
+ * For all standard scanners use {@code Scanners.values()}.
+ *
Classloader can optionally be used for resolving runtime classes from names.
*
- * Set<Method> someMethods = reflections.getMethodsMatchParams(long.class, int.class);
- * Set<Method> voidMethods = reflections.getMethodsReturn(void.class);
- * Set<Method> pathParamMethods = reflections.getMethodsWithAnyParamAnnotated(PathParam.class);
- * Set<Method> floatToString = reflections.getConverters(Float.class, String.class);
- * List<String> parameterNames = reflections.getMethodsParamNames(Method.class);
+ *
Query using {@link Reflections#get(QueryFunction)}, such as:
+ * {@code Set> modules = reflections.get(SubTypes.of(Module.class).asClass());
+ * Set> singletons = reflections.get(TypesAnnotated.with(Singleton.class).asClass());
+ * Set properties = reflections.get(Resources.with(".*\\.properties"));
+ * Set requests = reflections.get(MethodsAnnotated.with(RequestMapping.class).as(Method.class));
+ * Set voidMethods = reflections.get(MethodsReturn.with(void.class).as(Method.class));
+ * Set someMethods = reflections.get(MethodsSignature.of(long.class, int.class).as(Method.class));
+ * }
*
- * Set<Member> fieldUsage = reflections.getFieldUsage(Field.class);
- * Set<Member> methodUsage = reflections.getMethodUsage(Method.class);
- * Set<Member> constructorUsage = reflections.getConstructorUsage(Constructor.class);
- *
- * You can use other scanners defined in Reflections as well, such as: SubTypesScanner, TypeAnnotationsScanner (both default),
- * ResourcesScanner, MethodAnnotationsScanner, ConstructorAnnotationsScanner, FieldAnnotationsScanner,
- * MethodParameterScanner, MethodParameterNamesScanner, MemberUsageScanner or any custom scanner.
- *
Use {@link #getStore()} to access and query the store directly
- *
In order to save the store metadata, use {@link #save(String)} or {@link #save(String, org.reflections.serializers.Serializer)}
- * for example with {@link org.reflections.serializers.XmlSerializer} or {@link org.reflections.serializers.JavaCodeSerializer}
- *
In order to collect pre saved metadata and avoid re-scanning, use {@link #collect(String, java.util.function.Predicate, org.reflections.serializers.Serializer...)}}
- *
Make sure to scan all the transitively relevant packages.
- *
for instance, given your class C extends B extends A, and both B and A are located in another package than C,
- * when only the package of C is scanned - then querying for sub types of A returns nothing (transitive), but querying for sub types of B returns C (direct).
- * In that case make sure to scan all relevant packages a priori.
- *
For Javadoc, source code, and more information about Reflections Library, see http://github.com/ronmamo/reflections/
+ * If not using {@code asClass()} or {@code as()} query results are strings, such that:
+ *
{@code Set modules = reflections.get(SubTypes.of(Module.class));
+ * Set singletons = reflections.get(TypesAnnotated.with(Singleton.class));
+ * }
+ * Note that previous 0.9.x API is still supported, for example:
+ *
{@code Set> modules = reflections.getSubTypesOf(Module.class);
+ * Set> singletons = reflections.getTypesAnnotatedWith(Singleton.class);
+ * }
+ * Queries can combine {@link Scanners} and {@link ReflectionUtils} functions, and compose fluent functional methods from {@link QueryFunction}.
+ *
{@code }
+ * Scanned metadata can be saved using {@link #save(String)}, and collected using {@link #collect(String, java.util.function.Predicate, org.reflections.serializers.Serializer)}
+ *
+ * For Javadoc, source code, and more information about Reflections Library, see http://github.com/ronmamo/reflections/
*/
-public class Reflections {
- public static Logger log = findLogger(Reflections.class);
+public class Reflections implements NameHelper {
+ public final static Logger log = LoggerFactory.getLogger(Reflections.class);
protected final transient Configuration configuration;
- protected Store store;
+ protected final Store store;
/**
- * constructs a Reflections instance and scan according to given {@link org.reflections.Configuration}
- * it is preferred to use {@link org.reflections.util.ConfigurationBuilder}
+ * constructs Reflections instance and scan according to the given {@link org.reflections.Configuration}
+ *
it is preferred to use {@link org.reflections.util.ConfigurationBuilder}
{@code new Reflections(new ConfigurationBuilder()...)}
*/
- public Reflections(final Configuration configuration) {
+ public Reflections(Configuration configuration) {
this.configuration = configuration;
- store = new Store(configuration);
-
- if (configuration.getScanners() != null && !configuration.getScanners().isEmpty()) {
- //inject to scanners
- for (Scanner scanner : configuration.getScanners()) {
- scanner.setConfiguration(configuration);
- }
-
- scan();
-
- if (configuration.shouldExpandSuperTypes()) {
- expandSuperTypes();
- }
+ Map>> storeMap = scan();
+ if (configuration.shouldExpandSuperTypes()) {
+ expandSuperTypes(storeMap.get(SubTypes.index()));
}
+ store = new Store(storeMap);
+ }
+
+ public Reflections(Store store) {
+ this.configuration = new ConfigurationBuilder();
+ this.store = store;
}
/**
- * a convenient constructor for scanning within a package prefix.
- * this actually create a {@link org.reflections.Configuration} with:
- *
- urls that contain resources with name {@code prefix}
- *
- filterInputsBy where name starts with the given {@code prefix}
- *
- scanners set to the given {@code scanners}, otherwise defaults to {@link org.reflections.scanners.TypeAnnotationsScanner} and {@link org.reflections.scanners.SubTypesScanner}.
- * @param prefix package prefix, to be used with {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} )}
- * @param scanners optionally supply scanners, otherwise defaults to {@link org.reflections.scanners.TypeAnnotationsScanner} and {@link org.reflections.scanners.SubTypesScanner}
+ * constructs Reflections instance and scan according to the given package {@code prefix} and optional {@code scanners}
+ *
{@code new Reflections("org.reflections")}
+ * it is preferred to use {@link org.reflections.util.ConfigurationBuilder} instead, this is actually similar to:
+ *
{@code new Reflections(
+ * new ConfigurationBuilder()
+ * .forPackage(prefix)
+ * .setScanners(scanners))
+ * }
+ * uses {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} to resolve urls from given {@code prefix}
+ *
optional {@code scanners} defaults to {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes}
*/
- public Reflections(final String prefix, final Scanner... scanners) {
+ public Reflections(String prefix, Scanner... scanners) {
this((Object) prefix, scanners);
}
/**
- * a convenient constructor for Reflections, where given {@code Object...} parameter types can be either:
- *
- * - {@link String} - would add urls using {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} ()}
- * - {@link Class} - would add urls using {@link org.reflections.util.ClasspathHelper#forClass(Class, ClassLoader...)}
- * - {@link ClassLoader} - would use this classloaders in order to find urls in {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} and {@link org.reflections.util.ClasspathHelper#forClass(Class, ClassLoader...)}
- * - {@link org.reflections.scanners.Scanner} - would use given scanner, overriding the default scanners
- * - {@link java.net.URL} - would add the given url for scanning
- * - {@link Object[]} - would use each element as above
- *
- *
- * use any parameter type in any order. this constructor uses instanceof on each param and instantiate a {@link org.reflections.util.ConfigurationBuilder} appropriately.
- * if you prefer the usual statically typed constructor, don't use this, although it can be very useful.
- *
- *
for example:
- *
- * new Reflections("my.package", classLoader);
- * //or
- * new Reflections("my.package", someScanner, anotherScanner, classLoader);
- * //or
- * new Reflections(myUrl, myOtherUrl);
- *
- */
- public Reflections(final Object... params) {
+ * Convenient constructor for Reflections.
+ * see the javadoc of {@link ConfigurationBuilder#build(Object...)} for details.
+ * it is preferred to use {@link org.reflections.util.ConfigurationBuilder} instead. */
+ public Reflections(Object... params) {
this(ConfigurationBuilder.build(params));
}
protected Reflections() {
configuration = new ConfigurationBuilder();
- store = new Store(configuration);
+ store = new Store(new HashMap<>());
}
- //
- protected void scan() {
- if (configuration.getUrls() == null || configuration.getUrls().isEmpty()) {
- if (log != null) {
- log.warn("given scan urls are empty. set urls in the configuration");
- }
- return;
- }
-
- if (log != null && log.isTraceEnabled()) {
- log.trace("going to scan these urls: {}", configuration.getUrls());
- }
-
- long time = System.currentTimeMillis();
- int scannedUrls = 0;
- ExecutorService executorService = configuration.getExecutorService();
- List> futures = new ArrayList<>();
+ protected Map>> scan() {
+ long start = System.currentTimeMillis();
+ Map>> collect = configuration.getScanners().stream().map(Scanner::index).distinct()
+ .collect(Collectors.toMap(s -> s, s -> Collections.synchronizedSet(new HashSet<>())));
+ Set urls = configuration.getUrls();
- for (final URL url : configuration.getUrls()) {
+ (configuration.isParallel() ? urls.stream().parallel() : urls.stream()).forEach(url -> {
+ Vfs.Dir dir = null;
try {
- if (executorService != null) {
- futures.add(executorService.submit(() -> {
- if (log != null && log.isTraceEnabled()) {
- log.trace("[{}] scanning {}", Thread.currentThread().toString(), url);
+ dir = Vfs.fromURL(url);
+ for (Vfs.File file : dir.getFiles()) {
+ if (doFilter(file, configuration.getInputsFilter())) {
+ ClassFile classFile = null;
+ for (Scanner scanner : configuration.getScanners()) {
+ try {
+ if (doFilter(file, scanner::acceptsInput)) {
+ List> entries = scanner.scan(file);
+ if (entries == null) {
+ if (classFile == null) classFile = getClassFile(file);
+ entries = scanner.scan(classFile);
+ }
+ if (entries != null) collect.get(scanner.index()).addAll(entries);
+ }
+ } catch (Exception e) {
+ if (log != null) log.trace("could not scan file {} with scanner {}", file.getRelativePath(), scanner.getClass().getSimpleName(), e);
+ }
}
- scan(url);
- }));
- } else {
- scan(url);
- }
- scannedUrls++;
- } catch (ReflectionsException e) {
- if (log != null) {
- log.warn("could not create Vfs.Dir from url. ignoring the exception and continuing", e);
+ }
}
+ } catch (Exception e) {
+ if (log != null) log.warn("could not create Vfs.Dir from url. ignoring the exception and continuing", e);
+ } finally {
+ if (dir != null) dir.close();
}
- }
-
- if (executorService != null) {
- for (Future future : futures) {
- try {
- future.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RuntimeException(e);
- }
+ });
+
+ // merge
+ Map>> storeMap =
+ collect.entrySet().stream()
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ entry -> entry.getValue().stream().filter(e -> e.getKey() != null)
+ .collect(Collectors.groupingBy(
+ Map.Entry::getKey,
+ HashMap::new,
+ Collectors.mapping(Map.Entry::getValue, Collectors.toSet())))));
+ if (log != null) {
+ int keys = 0, values = 0;
+ for (Map> map : storeMap.values()) {
+ keys += map.size();
+ values += map.values().stream().mapToLong(Set::size).sum();
}
+ log.info(format("Reflections took %d ms to scan %d urls, producing %d keys and %d values", System.currentTimeMillis() - start, urls.size(), keys, values));
}
+ return storeMap;
+ }
- //gracefully shutdown the parallel scanner executor service.
- if (executorService != null) {
- executorService.shutdown();
- }
-
- if (log != null) {
- log.info(format("Reflections took %d ms to scan %d urls, producing %s %s",
- System.currentTimeMillis() - time, scannedUrls, producingDescription(store),
- executorService instanceof ThreadPoolExecutor ?
- format("[using %d cores]", ((ThreadPoolExecutor) executorService).getMaximumPoolSize()) : ""));
- }
+ private boolean doFilter(Vfs.File file, @Nullable Predicate predicate) {
+ String path = file.getRelativePath();
+ String fqn = path.replace('/', '.');
+ return predicate == null || predicate.test(path) || predicate.test(fqn);
}
- private static String producingDescription(Store store) {
- int keys = 0;
- int values = 0;
- for (String index : store.keySet()) {
- keys += store.keys(index).size();
- values += store.values(index).size();
- }
- return String.format("%d keys and %d values", keys, values);
- }
-
- protected void scan(URL url) {
- Vfs.Dir dir = Vfs.fromURL(url);
-
- try {
- for (final Vfs.File file : dir.getFiles()) {
- // scan if inputs filter accepts file relative path or fqn
- Predicate inputsFilter = configuration.getInputsFilter();
- String path = file.getRelativePath();
- String fqn = path.replace('/', '.');
- if (inputsFilter == null || inputsFilter.test(path) || inputsFilter.test(fqn)) {
- Object classObject = null;
- for (Scanner scanner : configuration.getScanners()) {
- try {
- if (scanner.acceptsInput(path) || scanner.acceptsInput(fqn)) {
- classObject = scanner.scan(file, classObject, store);
- }
- } catch (Exception e) {
- if (log != null && log.isTraceEnabled()) {
- // SLF4J will filter out Throwables from the format string arguments.
- log.trace("could not scan file {} in url {} with scanner {}", file.getRelativePath(), url.toExternalForm(), scanner.getClass().getSimpleName(), e);
- }
- }
- }
- }
- }
- } finally {
- dir.close();
+ private ClassFile getClassFile(Vfs.File file) {
+ try (DataInputStream dis = new DataInputStream(new BufferedInputStream(file.openInputStream()))) {
+ return new ClassFile(dis);
+ } catch (Exception e) {
+ throw new ReflectionsException("could not create class object from file " + file.getRelativePath(), e);
}
}
@@ -294,344 +238,357 @@ protected void scan(URL url) {
* and includes files matching the pattern .*-reflections.xml
* */
public static Reflections collect() {
- return collect("META-INF/reflections/", new FilterBuilder().include(".*-reflections.xml"));
+ return collect("META-INF/reflections/", new FilterBuilder().includePattern(".*-reflections\\.xml"));
}
/**
- * collect saved Reflections resources from all urls that contains the given packagePrefix and matches the given resourceNameFilter
- * and de-serializes them using the default serializer {@link org.reflections.serializers.XmlSerializer} or using the optionally supplied optionalSerializer
- *
- * it is preferred to use a designated resource prefix (for example META-INF/reflections but not just META-INF),
- * so that relevant urls could be found much faster
- * @param optionalSerializer - optionally supply one serializer instance. if not specified or null, {@link org.reflections.serializers.XmlSerializer} will be used
+ * collect saved Reflections metadata from all urls that contains the given {@code packagePrefix} and matches the given {@code resourceNameFilter},
+ * and deserialize using the default serializer {@link org.reflections.serializers.XmlSerializer}
+ *
{@code Reflections.collect("META-INF/reflections/",
+ * new FilterBuilder().includePattern(".*-reflections\\.xml")}
+ * prefer using a designated directory (for example META-INF/reflections but not just META-INF), so that collect can work much faster
*/
- public static Reflections collect(final String packagePrefix, final Predicate resourceNameFilter, Serializer... optionalSerializer) {
- Serializer serializer = optionalSerializer != null && optionalSerializer.length == 1 ? optionalSerializer[0] : new XmlSerializer();
+ public static Reflections collect(String packagePrefix, Predicate resourceNameFilter) {
+ return collect(packagePrefix, resourceNameFilter, new XmlSerializer());
+ }
+ /**
+ * collect saved Reflections metadata from all urls that contains the given {@code packagePrefix} and matches the given {@code resourceNameFilter},
+ * and deserializes using the given {@code serializer}
+ * {@code Reflections reflections = Reflections.collect(
+ * "META-INF/reflections/",
+ * new FilterBuilder().includePattern(".*-reflections\\.xml"),
+ * new XmlSerializer())}
+ * prefer using a designated directory (for example META-INF/reflections but not just META-INF), so that collect can work much faster
+ */
+ public static Reflections collect(String packagePrefix, Predicate resourceNameFilter, Serializer serializer) {
Collection urls = ClasspathHelper.forPackage(packagePrefix);
- if (urls.isEmpty()) return null;
- long start = System.currentTimeMillis();
- final Reflections reflections = new Reflections();
Iterable files = Vfs.findFiles(urls, packagePrefix, resourceNameFilter);
- for (final Vfs.File file : files) {
- InputStream inputStream = null;
- try {
- inputStream = file.openInputStream();
- reflections.merge(serializer.read(inputStream));
- } catch (IOException e) {
- throw new ReflectionsException("could not merge " + file, e);
- } finally {
- close(inputStream);
- }
- }
-
- if (log != null) {
- log.info(format("Reflections took %d ms to collect %d url, producing %s",
- System.currentTimeMillis() - start, urls.size(), producingDescription(reflections.store)));
- }
+ Reflections reflections = new Reflections();
+ StreamSupport.stream(files.spliterator(), false)
+ .forEach(file -> {
+ try (InputStream inputStream = file.openInputStream()) {
+ reflections.collect(inputStream, serializer);
+ } catch (IOException e) {
+ throw new ReflectionsException("could not merge " + file, e);
+ }
+ });
return reflections;
}
- /** merges saved Reflections resources from the given input stream, using the serializer configured in this instance's Configuration
- *
useful if you know the serialized resource location and prefer not to look it up the classpath
- * */
- public Reflections collect(final InputStream inputStream) {
- try {
- merge(configuration.getSerializer().read(inputStream));
- } catch (Exception ex) {
- throw new ReflectionsException("could not merge input stream", ex);
- }
-
- return this;
+ /**
+ * deserialize and merge saved Reflections metadata from the given {@code inputStream} and {@code serializer}
+ * useful if you know the serialized resource location and prefer not to look it up the classpath
+ */
+ public Reflections collect(InputStream inputStream, Serializer serializer) {
+ return merge(serializer.read(inputStream));
}
- /** merges saved Reflections resources from the given file, using the serializer configured in this instance's Configuration
- *
useful if you know the serialized resource location and prefer not to look it up the classpath
- * */
- public Reflections collect(final File file) {
- FileInputStream inputStream = null;
- try {
- inputStream = new FileInputStream(file);
- return collect(inputStream);
- } catch (FileNotFoundException e) {
+ /**
+ * deserialize and merge saved Reflections metadata from the given {@code file} and {@code serializer}
+ *
useful if you know the serialized resource location and prefer not to look it up the classpath
+ */
+ public Reflections collect(File file, Serializer serializer) {
+ try (FileInputStream inputStream = new FileInputStream(file)) {
+ return collect(inputStream, serializer);
+ } catch (IOException e) {
throw new ReflectionsException("could not obtain input stream from file " + file, e);
- } finally {
- Utils.close(inputStream);
}
}
- /**
- * merges a Reflections instance metadata into this instance
- */
- public Reflections merge(final Reflections reflections) {
- store.merge(reflections.store);
+ /** merges the given {@code reflections} instance metadata into this instance */
+ public Reflections merge(Reflections reflections) {
+ reflections.store.forEach((index, map) -> this.store.merge(index, map, (m1, m2) -> {
+ m2.forEach((k, v) -> m1.merge(k, v, (s1, s2) -> { s1.addAll(s2); return s1;}));
+ return m1;
+ }));
return this;
}
/**
* expand super types after scanning, for super types that were not scanned.
- * this is helpful in finding the transitive closure without scanning all 3rd party dependencies.
- * it uses {@link ReflectionUtils#getSuperTypes(Class)}.
- *
- * for example, for classes A,B,C where A supertype of B, B supertype of C:
+ *
this is helpful in finding the transitive closure without scanning all 3rd party dependencies.
+ *
+ * for example, for classes A,B,C where A supertype of B, B supertype of C (A -> B -> C):
*
- * - if scanning C resulted in B (B->C in store), but A was not scanned (although A supertype of B) - then getSubTypes(A) will not return C
+ * - if scanning C resulted in B (B->C in store), but A was not scanned (although A is a supertype of B) - then getSubTypes(A) will not return C
* - if expanding supertypes, B will be expanded with A (A->B in store) - then getSubTypes(A) will return C
*
*/
- public void expandSuperTypes() {
- String index = index(SubTypesScanner.class);
- Set keys = store.keys(index);
- keys.removeAll(store.values(index));
+ public void expandSuperTypes(Map> map) {
+ if (map == null || map.isEmpty()) return;
+ Set keys = new LinkedHashSet<>(map.keySet());
+ keys.removeAll(map.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
+ keys.remove("java.lang.Object");
for (String key : keys) {
- final Class> type = forName(key, loaders());
+ Class> type = forClass(key, loaders());
if (type != null) {
- expandSupertypes(store, key, type);
+ expandSupertypes(map, key, type);
}
}
}
- private void expandSupertypes(Store store, String key, Class> type) {
+ private void expandSupertypes(Map> map, String key, Class> type) {
for (Class> supertype : ReflectionUtils.getSuperTypes(type)) {
- if (store.put(SubTypesScanner.class, supertype.getName(), key)) {
- if (log != null && log.isTraceEnabled()) {
- log.trace("expanded subtype {} -> {}", supertype.getName(), key);
- }
- expandSupertypes(store, supertype.getName(), supertype);
+ String supertypeName = supertype.getName();
+ if (!map.containsKey(supertypeName)) {
+ map.computeIfAbsent(supertypeName, s -> new HashSet<>()).add(key);
+ expandSupertypes(map, supertypeName, supertype);
}
}
}
- //query
/**
- * gets all sub types in hierarchy of a given type
- * depends on SubTypesScanner configured
+ * apply {@link QueryFunction} on {@link Store}
+ * {@code Set ts = get(query)}
+ * use {@link Scanners} and {@link ReflectionUtils} query functions, such as:
+ *
{@code
+ * Set annotated = get(Scanners.TypesAnnotated.with(A.class))
+ * Set> subtypes = get(Scanners.SubTypes.of(B.class).asClass())
+ * Set methods = get(ReflectionUtils.Methods.of(B.class))
+ * }
*/
- public Set> getSubTypesOf(final Class type) {
- return forNames(store.getAll(SubTypesScanner.class, type.getName()), loaders());
+ public Set get(QueryFunction query) {
+ return query.apply(store);
}
/**
- * get types annotated with a given annotation, both classes and annotations
- * {@link java.lang.annotation.Inherited} is not honored by default.
- *
when honoring @Inherited, meta-annotation should only effect annotated super classes and its sub types
- *
Note that this (@Inherited) meta-annotation type has no effect if the annotated type is used for anything other then a class.
- * Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect.
- *
depends on TypeAnnotationsScanner and SubTypesScanner configured
+ * gets all subtypes in hierarchy of a given {@code type}.
+ * similar to {@code get(SubTypes.of(type))}
+ *
depends on {@link Scanners#SubTypes} configured
+ */
+ public Set> getSubTypesOf(Class type) {
+ //noinspection unchecked
+ return (Set>) get(SubTypes.of(type)
+ .as((Class extends T>) Class.class, loaders()));
+ }
+
+ /**
+ * get types annotated with the given {@code annotation}, both classes and annotations
+ * {@link java.lang.annotation.Inherited} is not honored by default, see {@link #getTypesAnnotatedWith(Class, boolean)}.
+ *
similar to {@code get(SubTypes.of(TypesAnnotated.with(annotation)))}
+ *
depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured
*/
- public Set> getTypesAnnotatedWith(final Class extends Annotation> annotation) {
- return getTypesAnnotatedWith(annotation, false);
+ public Set> getTypesAnnotatedWith(Class extends Annotation> annotation) {
+ return get(SubTypes.of(TypesAnnotated.with(annotation)).asClass(loaders()));
}
/**
- * get types annotated with a given annotation, both classes and annotations
- * {@link java.lang.annotation.Inherited} is honored according to given honorInherited.
- *
when honoring @Inherited, meta-annotation should only effect annotated super classes and it's sub types
+ * get types annotated with the given {@code annotation}, both classes and annotations
+ *
{@link java.lang.annotation.Inherited} is honored according to the given {@code honorInherited}.
+ *
when honoring @Inherited, meta-annotation should only effect annotated super classes and subtypes
*
when not honoring @Inherited, meta annotation effects all subtypes, including annotations interfaces and classes
*
Note that this (@Inherited) meta-annotation type has no effect if the annotated type is used for anything other then a class.
* Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect.
- *
depends on TypeAnnotationsScanner and SubTypesScanner configured
+ * depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured
*/
- public Set> getTypesAnnotatedWith(final Class extends Annotation> annotation, boolean honorInherited) {
- Set annotated = store.get(TypeAnnotationsScanner.class, annotation.getName());
- annotated.addAll(getAllAnnotated(annotated, annotation, honorInherited));
- return forNames(annotated, loaders());
+ public Set> getTypesAnnotatedWith(Class extends Annotation> annotation, boolean honorInherited) {
+ if (!honorInherited) {
+ return getTypesAnnotatedWith(annotation);
+ } else {
+ if (annotation.isAnnotationPresent(Inherited.class)) {
+ return get(TypesAnnotated.get(annotation)
+ .add(SubTypes.of(TypesAnnotated.get(annotation)
+ .filter(c -> !forClass(c, loaders()).isInterface())))
+ .asClass(loaders()));
+ } else {
+ return get(TypesAnnotated.get(annotation).asClass(loaders()));
+ }
+ }
}
/**
- * get types annotated with a given annotation, both classes and annotations, including annotation member values matching
- * {@link java.lang.annotation.Inherited} is not honored by default
- *
depends on TypeAnnotationsScanner configured
+ * get types annotated with the given {@code annotation}, both classes and annotations, including annotation member values matching
+ * {@link java.lang.annotation.Inherited} is not honored by default, see {@link #getTypesAnnotatedWith(Annotation, boolean)}.
+ *
depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured
*/
- public Set> getTypesAnnotatedWith(final Annotation annotation) {
- return getTypesAnnotatedWith(annotation, false);
+ public Set> getTypesAnnotatedWith(Annotation annotation) {
+ return get(SubTypes.of(
+ TypesAnnotated.of(TypesAnnotated.get(annotation.annotationType())
+ .filter(c -> withAnnotation(annotation).test(forClass(c, loaders())))))
+ .asClass(loaders()));
}
/**
- * get types annotated with a given annotation, both classes and annotations, including annotation member values matching
+ * get types annotated with the given {@code annotation}, both classes and annotations, including annotation member values matching
* {@link java.lang.annotation.Inherited} is honored according to given honorInherited
- *
depends on TypeAnnotationsScanner configured
+ * depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured
*/
- public Set> getTypesAnnotatedWith(final Annotation annotation, boolean honorInherited) {
- Set annotated = store.get(TypeAnnotationsScanner.class, annotation.annotationType().getName());
- Set> allAnnotated = filter(forNames(annotated, loaders()), withAnnotation(annotation));
- Set> classes = forNames(filter(getAllAnnotated(names(allAnnotated), annotation.annotationType(), honorInherited), s -> !annotated.contains(s)), loaders());
- allAnnotated.addAll(classes);
- return allAnnotated;
- }
-
- protected Collection getAllAnnotated(Collection annotated, Class extends Annotation> annotation, boolean honorInherited) {
- if (honorInherited) {
- if (annotation.isAnnotationPresent(Inherited.class)) {
- Set subTypes = store.get(SubTypesScanner.class, filter(annotated, input -> {
- final Class> type = forName(input, loaders());
- return type != null && !type.isInterface();
- }));
- return store.getAllIncluding(SubTypesScanner.class, subTypes);
+ public Set> getTypesAnnotatedWith(Annotation annotation, boolean honorInherited) {
+ if (!honorInherited) {
+ return getTypesAnnotatedWith(annotation);
+ } else {
+ Class extends Annotation> type = annotation.annotationType();
+ if (type.isAnnotationPresent(Inherited.class)) {
+ return get(TypesAnnotated.with(type).asClass(loaders()).filter(withAnnotation(annotation))
+ .add(SubTypes.of(TypesAnnotated.with(type).asClass(loaders()).filter(c -> !c.isInterface()))));
} else {
- return annotated;
+ return get(TypesAnnotated.with(type).asClass(loaders()).filter(withAnnotation(annotation)));
}
- } else {
- Collection subTypes = store.getAllIncluding(TypeAnnotationsScanner.class, annotated);
- return store.getAllIncluding(SubTypesScanner.class, subTypes);
}
}
/**
- * get all methods annotated with a given annotation
- * depends on MethodAnnotationsScanner configured
+ * get methods annotated with the given {@code annotation}
+ * similar to {@code get(MethodsAnnotated.with(annotation))}
+ *
depends on {@link Scanners#MethodsAnnotated} configured
*/
- public Set getMethodsAnnotatedWith(final Class extends Annotation> annotation) {
- return getMethodsFromDescriptors(store.get(MethodAnnotationsScanner.class, annotation.getName()), loaders());
+ public Set getMethodsAnnotatedWith(Class extends Annotation> annotation) {
+ return get(MethodsAnnotated.with(annotation).as(Method.class, loaders()));
}
/**
- * get all methods annotated with a given annotation, including annotation member values matching
- * depends on MethodAnnotationsScanner configured
+ * get methods annotated with the given {@code annotation}, including annotation member values matching
+ * similar to {@code get(MethodsAnnotated.with(annotation))}
+ *
depends on {@link Scanners#MethodsAnnotated} configured
*/
- public Set getMethodsAnnotatedWith(final Annotation annotation) {
- return filter(getMethodsAnnotatedWith(annotation.annotationType()), withAnnotation(annotation));
- }
-
- /** get methods with parameter types matching given {@code types}*/
- public Set getMethodsMatchParams(Class>... types) {
- return getMethodsFromDescriptors(store.get(MethodParameterScanner.class, names(types).toString()), loaders());
- }
-
- /** get methods with return type match given type */
- public Set getMethodsReturn(Class returnType) {
- return getMethodsFromDescriptors(store.get(MethodParameterScanner.class, names(returnType)), loaders());
- }
-
- /** get methods with any parameter annotated with given annotation */
- public Set getMethodsWithAnyParamAnnotated(Class extends Annotation> annotation) {
- return getMethodsFromDescriptors(store.get(MethodParameterScanner.class, annotation.getName()), loaders());
-
- }
-
- /** get methods with any parameter annotated with given annotation, including annotation member values matching */
- public Set getMethodsWithAnyParamAnnotated(Annotation annotation) {
- return filter(getMethodsWithAnyParamAnnotated(annotation.annotationType()), withAnyParameterAnnotation(annotation));
+ public Set getMethodsAnnotatedWith(Annotation annotation) {
+ return get(MethodsAnnotated.with(annotation.annotationType()).as(Method.class, loaders())
+ .filter(withAnnotation(annotation)));
}
/**
- * get all constructors annotated with a given annotation
- * depends on MethodAnnotationsScanner configured
+ * get methods with signature matching the given {@code types}
+ * similar to {@code get(MethodsSignature.of(types))}
+ *
depends on {@link Scanners#MethodsSignature} configured
*/
- public Set getConstructorsAnnotatedWith(final Class extends Annotation> annotation) {
- return getConstructorsFromDescriptors(store.get(MethodAnnotationsScanner.class, annotation.getName()), loaders());
+ public Set getMethodsWithSignature(Class>... types) {
+ return get(MethodsSignature.with(types).as(Method.class, loaders()));
}
/**
- * get all constructors annotated with a given annotation, including annotation member values matching
- * depends on MethodAnnotationsScanner configured
+ * get methods with any parameter matching the given {@code type}, either class or annotation
+ * similar to {@code get(MethodsParameter.with(type))}
+ *
depends on {@link Scanners#MethodsParameter} configured
*/
- public Set getConstructorsAnnotatedWith(final Annotation annotation) {
- return filter(getConstructorsAnnotatedWith(annotation.annotationType()), withAnnotation(annotation));
+ public Set getMethodsWithParameter(AnnotatedElement type) {
+ return get(MethodsParameter.with(type).as(Method.class, loaders()));
}
- /** get constructors with parameter types matching given {@code types}*/
- public Set getConstructorsMatchParams(Class>... types) {
- return getConstructorsFromDescriptors(store.get(MethodParameterScanner.class, names(types).toString()), loaders());
+ /**
+ * get methods with return type matching the given {@code returnType}
+ * similar to {@code get(MethodsReturn.of(type))}
+ *
depends on {@link Scanners#MethodsParameter} configured
+ */
+ public Set getMethodsReturn(Class> type) {
+ return get(MethodsReturn.of(type).as(Method.class, loaders()));
}
- /** get constructors with any parameter annotated with given annotation */
- public Set getConstructorsWithAnyParamAnnotated(Class extends Annotation> annotation) {
- return getConstructorsFromDescriptors(store.get(MethodParameterScanner.class, annotation.getName()), loaders());
+ /**
+ * get constructors annotated with the given {@code annotation}
+ * similar to {@code get(ConstructorsAnnotated.with(annotation))}
+ *
depends on {@link Scanners#ConstructorsAnnotated} configured
+ */
+ public Set getConstructorsAnnotatedWith(Class extends Annotation> annotation) {
+ return get(ConstructorsAnnotated.with(annotation).as(Constructor.class, loaders()));
}
- /** get constructors with any parameter annotated with given annotation, including annotation member values matching */
- public Set getConstructorsWithAnyParamAnnotated(Annotation annotation) {
- return filter(getConstructorsWithAnyParamAnnotated(annotation.annotationType()), withAnyParameterAnnotation(annotation));
+ /**
+ * get constructors annotated with the given {@code annotation}, including annotation member values matching
+ * similar to {@code get(ConstructorsAnnotated.with(annotation))}
+ *
depends on {@link Scanners#ConstructorsAnnotated} configured
+ */
+ public Set getConstructorsAnnotatedWith(Annotation annotation) {
+ return get(ConstructorsAnnotated.with(annotation.annotationType()).as(Constructor.class, loaders())
+ .filter(withAnyParameterAnnotation(annotation)));
}
/**
- * get all fields annotated with a given annotation
- * depends on FieldAnnotationsScanner configured
+ * get constructors with signature matching the given {@code types}
+ * similar to {@code get(ConstructorsSignature.with(types))}
+ *
depends on {@link Scanners#ConstructorsSignature} configured
*/
- public Set getFieldsAnnotatedWith(final Class extends Annotation> annotation) {
- return store.get(FieldAnnotationsScanner.class, annotation.getName()).stream()
- .map(annotated -> getFieldFromString(annotated, loaders()))
- .collect(Collectors.toSet());
+ public Set getConstructorsWithSignature(Class>... types) {
+ return get(ConstructorsSignature.with(types).as(Constructor.class, loaders()));
}
/**
- * get all methods annotated with a given annotation, including annotation member values matching
- * depends on FieldAnnotationsScanner configured
+ * get constructors with any parameter matching the given {@code type}, either class or annotation
+ * similar to {@code get(ConstructorsParameter.with(types))}
+ *
depends on {@link Scanners#ConstructorsParameter} configured
*/
- public Set getFieldsAnnotatedWith(final Annotation annotation) {
- return filter(getFieldsAnnotatedWith(annotation.annotationType()), withAnnotation(annotation));
+ public Set getConstructorsWithParameter(AnnotatedElement type) {
+ return get(ConstructorsParameter.of(type).as(Constructor.class, loaders()));
}
- /** get resources relative paths where simple name (key) matches given namePredicate
- * depends on ResourcesScanner configured
- * */
- public Set