ReflectionUtils.java
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.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
/**
* utils for querying java reflection meta types
* <p>see {@link #SuperTypes}, {@link #Annotations}, {@link #AnnotationTypes}, {@link #Methods}, {@link #Constructors} and {@link #Fields}.
* <pre>{@code
* Set<Class<?>> supertypes = get(SuperTypes.of(type))
* Set<Annotation> annotations = get(Annotations.of(type))
* }</pre>
* <p>generally, apply {@link #get(QueryFunction)} on {@link QueryFunction} created by {@link UtilQueryBuilder}, and optionally use the functional methods in QueryFunction.
* <pre>{@code get(Methods.of(type)
* .filter(withPublic().and(withPrefix("get")).and(withParameterCount(0)))
* .as(Method.class)
* .map(m -> ...))
* }</pre>
* <p>or (previously), use {@code getAllXXX(type/s, withYYY)} methods:
* <pre>{@code getAllSuperTypes(), getAllFields(), getAllMethods(), getAllConstructors() }
* </pre>
* <p>
* some predicates included here:
* <ul>
* <li>{@link #withPublic()}
* <li>{@link #withParametersCount(int)}}
* <li>{@link #withAnnotation(java.lang.annotation.Annotation)}
* <li>{@link #withParameters(Class[])}
* <li>{@link #withModifier(int)}
* <li>{@link #withReturnType(Class)}
* </ul>
* <pre>{@code
* import static org.reflections.ReflectionUtils.*;
*
* Set<Method> 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()...
* }</pre>
* */
@SuppressWarnings({"unchecked", "rawtypes"})
public abstract class ReflectionUtils extends ReflectionUtilsPredicates {
/** get type elements {@code <T>} by applying {@link QueryFunction} <pre>{@code get(SuperTypes.of(type))}</pre> */
public static <C, T> Set<T> get(QueryFunction<C, T> function) {
return function.apply(null);
}
/** get type elements {@code <T>} by applying {@link QueryFunction} and {@code predicates} */
public static <T> Set<T> get(QueryFunction<Store, T> queryFunction, Predicate<? super T>... predicates) {
return get(queryFunction.filter(Arrays.stream((Predicate[]) predicates).reduce(t -> true, Predicate::and)));
}
private static final List<String> objectMethodNames =
Arrays.asList("equals", "hashCode", "toString", "wait", "notify", "notifyAll");
/** predicate to filter out {@code Object} methods */
public static final Predicate<Method> notObjectMethod = m -> !objectMethodNames.contains(m.getName());
/** query super class <pre>{@code get(SuperClass.of(element)) -> Set<Class<?>>}</pre>
* <p>see also {@link ReflectionUtils#SuperTypes}, {@link ReflectionUtils#Interfaces} */
public static final UtilQueryBuilder<Class<?>, Class<?>> SuperClass =
element -> ctx -> {
Class<?> superclass = element.getSuperclass();
return superclass != null && !superclass.equals(Object.class) ? Collections.singleton(superclass) : Collections.emptySet();
};
/** query interfaces <pre>{@code get(Interfaces.of(element)) -> Set<Class<?>>}</pre> */
public static final UtilQueryBuilder<Class<?>, Class<?>> Interfaces =
element -> ctx -> Stream.of(element.getInterfaces()).collect(Collectors.toCollection(LinkedHashSet::new));
/** query super classes and interfaces including element <pre>{@code get(SuperTypes.of(element)) -> Set<Class<?>> }</pre> */
public static final UtilQueryBuilder<Class<?>, Class<?>> SuperTypes =
new UtilQueryBuilder<Class<?>, Class<?>>() {
@Override
public QueryFunction<Store, Class<?>> get(Class<?> element) {
return SuperClass.get(element).add(Interfaces.get(element));
}
@Override
public QueryFunction<Store, Class<?>> of(Class<?> element) {
return QueryFunction.<Store, Class<?>>single(element).getAll(SuperTypes::get);
}
};
/** query annotations <pre>{@code get(Annotation.of(element)) -> Set<Annotation> }</pre> */
public static final UtilQueryBuilder<AnnotatedElement, Annotation> Annotations =
new UtilQueryBuilder<AnnotatedElement, Annotation>() {
@Override
public QueryFunction<Store, Annotation> get(AnnotatedElement element) {
return ctx -> Arrays.stream(element.getAnnotations()).collect(Collectors.toCollection(LinkedHashSet::new));
}
@Override
public QueryFunction<Store, Annotation> of(AnnotatedElement element) {
return ReflectionUtils.extendType().get(element).getAll(Annotations::get, Annotation::annotationType);
}
};
/** query annotation types <pre>{@code get(AnnotationTypes.of(element)) -> Set<Class<? extends Annotation>> }</pre> */
public static final UtilQueryBuilder<AnnotatedElement, Class<? extends Annotation>> AnnotationTypes =
new UtilQueryBuilder<AnnotatedElement, Class<? extends Annotation>>() {
@Override
public QueryFunction<Store, Class<? extends Annotation>> get(AnnotatedElement element) {
return Annotations.get(element).map(Annotation::annotationType);
}
@Override
public QueryFunction<Store, Class<? extends Annotation>> of(AnnotatedElement element) {
return ReflectionUtils.extendType().get(element).getAll(AnnotationTypes::get, a -> a);
}
};
/** query methods <pre>{@code get(Methods.of(type)) -> Set<Method>}</pre> */
public static final UtilQueryBuilder<Class<?>, Method> Methods =
element -> ctx -> Arrays.stream(element.getDeclaredMethods()).filter(notObjectMethod).collect(Collectors.toCollection(LinkedHashSet::new));
/** query constructors <pre>{@code get(Constructors.of(type)) -> Set<Constructor> }</pre> */
public static final UtilQueryBuilder<Class<?>, Constructor> Constructors =
element -> ctx -> Arrays.<Constructor>stream(element.getDeclaredConstructors()).collect(Collectors.toCollection(LinkedHashSet::new));
/** query fields <pre>{@code get(Fields.of(type)) -> Set<Field> }</pre> */
public static final UtilQueryBuilder<Class<?>, Field> Fields =
element -> ctx -> Arrays.stream(element.getDeclaredFields()).collect(Collectors.toCollection(LinkedHashSet::new));
/** query url resources using {@link ClassLoader#getResources(java.lang.String)} <pre>{@code get(Resources.with(name)) -> Set<URL> }</pre> */
public static final UtilQueryBuilder<String, URL> Resources =
element -> ctx -> new HashSet<>(ClasspathHelper.forResource(element));
public static <T extends AnnotatedElement> UtilQueryBuilder<AnnotatedElement, T> extendType() {
return element -> {
if (element instanceof Class && !((Class<?>) element).isAnnotation()) {
QueryFunction<Store, Class<?>> single = QueryFunction.single((Class<?>) element);
return (QueryFunction<Store, T>) single.add(single.getAll(SuperTypes::get));
} else {
return QueryFunction.single((T) element);
}
};
}
/** get all annotations of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Annotations.of())} */
public static <T extends AnnotatedElement> Set<Annotation> getAllAnnotations(T type, Predicate<Annotation>... predicates) {
return get(Annotations.of(type), predicates);
}
/** get all super types of given {@code type}, including, optionally filtered by {@code predicates} */
public static Set<Class<?>> 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);
}
/** get the immediate supertype and interfaces of the given {@code type}
* <p>marked for removal, use instead {@code get(SuperTypes.get())} */
public static Set<Class<?>> getSuperTypes(Class<?> type) {
return get(SuperTypes.get(type));
}
/** get all methods of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Methods.of())} */
public static Set<Method> getAllMethods(final Class<?> type, Predicate<? super Method>... predicates) {
return get(Methods.of(type), predicates);
}
/** get methods of given {@code type}, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Methods.get())} */
public static Set<Method> getMethods(Class<?> t, Predicate<? super Method>... predicates) {
return get(Methods.get(t), predicates);
}
/** get all constructors of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Constructors.of())} */
public static Set<Constructor> getAllConstructors(final Class<?> type, Predicate<? super Constructor>... predicates) {
return get(Constructors.of(type), predicates);
}
/** get constructors of given {@code type}, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Constructors.get())} */
public static Set<Constructor> getConstructors(Class<?> t, Predicate<? super Constructor>... predicates) {
return get(Constructors.get(t), predicates);
}
/** get all fields of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Fields.of())} */
public static Set<Field> getAllFields(final Class<?> type, Predicate<? super Field>... predicates) {
return get(Fields.of(type), predicates);
}
/** get fields of given {@code type}, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Fields.get())} */
public static Set<Field> getFields(Class<?> type, Predicate<? super Field>... predicates) {
return get(Fields.get(type), predicates);
}
/** get annotations of given {@code type}, optionally honorInherited, optionally filtered by {@code predicates}
* <p>marked for removal, use instead {@code get(Annotations.get())} */
public static <T extends AnnotatedElement> Set<Annotation> getAnnotations(T type, Predicate<Annotation>... predicates) {
return get(Annotations.get(type), predicates);
}
/** map {@code annotation} to hash map of member values recursively <pre>{@code Annotations.of(type).map(ReflectionUtils::toMap)} </pre>*/
public static Map<String, Object> 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;
}));
}
/** map {@code annotation} and {@code annotatedElement} to hash map of member values
* <pre>{@code Annotations.of(type).map(a -> toMap(type, a))} </pre>*/
public static Map<String, Object> toMap(Annotation annotation, AnnotatedElement element) {
Map<String, Object> map = toMap(annotation);
if (element != null) map.put("annotatedElement", element);
return map;
}
/** create new annotation proxy with member values from the given {@code map} <pre>{@code toAnnotation(Map.of("annotationType", annotationType, "value", ""))}</pre> */
public static Annotation toAnnotation(Map<String, Object> map) {
return toAnnotation(map, (Class<? extends Annotation>) map.get("annotationType"));
}
/** create new annotation proxy with member values from the given {@code map} and member values from the given {@code map}
* <pre>{@code toAnnotation(Map.of("value", ""), annotationType)}</pre> */
public static <T extends Annotation> T toAnnotation(Map<String, Object> map, Class<T> annotationType) {
return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class<?>[]{annotationType},
(proxy, method, args) -> notObjectMethod.test(method) ? map.get(method.getName()) : method.invoke(map));
}
/** 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;
}
}
}