|
| 1 | +package ru.objectsfill.utils.reflection; |
| 2 | + |
| 3 | + |
| 4 | +import ru.objectsfill.utils.reflection.util.ClasspathHelper; |
| 5 | +import ru.objectsfill.utils.reflection.util.QueryFunction; |
| 6 | +import ru.objectsfill.utils.reflection.util.ReflectionUtilsPredicates; |
| 7 | +import ru.objectsfill.utils.reflection.util.UtilQueryBuilder; |
| 8 | + |
| 9 | +import java.lang.annotation.Annotation; |
| 10 | +import java.lang.reflect.AnnotatedElement; |
| 11 | +import java.lang.reflect.Constructor; |
| 12 | +import java.lang.reflect.Field; |
| 13 | +import java.lang.reflect.Method; |
| 14 | +import java.lang.reflect.Proxy; |
| 15 | +import java.net.URL; |
| 16 | +import java.util.Arrays; |
| 17 | +import java.util.Collections; |
| 18 | +import java.util.HashSet; |
| 19 | +import java.util.LinkedHashSet; |
| 20 | +import java.util.List; |
| 21 | +import java.util.Map; |
| 22 | +import java.util.Set; |
| 23 | +import java.util.function.Predicate; |
| 24 | +import java.util.stream.Collectors; |
| 25 | +import java.util.stream.Stream; |
| 26 | + |
| 27 | +import static java.util.stream.Collectors.toList; |
| 28 | + |
| 29 | +/** |
| 30 | + * utils for querying java reflection meta types |
| 31 | + * <p>see {@link #SuperTypes}, {@link #Annotations}, {@link #AnnotationTypes}, {@link #Methods}, {@link #Constructors} and {@link #Fields}. |
| 32 | + * <pre>{@code |
| 33 | + * Set<Class<?>> supertypes = get(SuperTypes.of(type)) |
| 34 | + * Set<Annotation> annotations = get(Annotations.of(type)) |
| 35 | + * }</pre> |
| 36 | + * <p>generally, apply {@link #get(QueryFunction)} on {@link QueryFunction} created by {@link UtilQueryBuilder}, and optionally use the functional methods in QueryFunction. |
| 37 | + * <pre>{@code get(Methods.of(type) |
| 38 | + * .filter(withPublic().and(withPrefix("get")).and(withParameterCount(0))) |
| 39 | + * .as(Method.class) |
| 40 | + * .map(m -> ...)) |
| 41 | + * }</pre> |
| 42 | + * <p>or (previously), use {@code getAllXXX(type/s, withYYY)} methods: |
| 43 | + * <pre>{@code getAllSuperTypes(), getAllFields(), getAllMethods(), getAllConstructors() } |
| 44 | + * </pre> |
| 45 | + * <p> |
| 46 | + * some predicates included here: |
| 47 | + * <ul> |
| 48 | + * <li>{@link #withPublic()} |
| 49 | + * <li>{@link #withParametersCount(int)}} |
| 50 | + * <li>{@link #withAnnotation(Annotation)} |
| 51 | + * <li>{@link #withParameters(Class[])} |
| 52 | + * <li>{@link #withModifier(int)} |
| 53 | + * <li>{@link #withReturnType(Class)} |
| 54 | + * </ul> |
| 55 | + * <pre>{@code |
| 56 | + * import static org.reflections.ReflectionUtils.*; |
| 57 | + * |
| 58 | + * Set<Method> getters = |
| 59 | + * get(Methods(classes) |
| 60 | + * .filter(withModifier(Modifier.PUBLIC).and(withPrefix("get")).and(withParametersCount(0))); |
| 61 | + * |
| 62 | + * get(Annotations.of(method) |
| 63 | + * .filter(withAnnotation()) |
| 64 | + * .map(annotation -> Methods.of(annotation) |
| 65 | + * .map(method -> ))))) |
| 66 | + * .stream()... |
| 67 | + * }</pre> |
| 68 | + * */ |
| 69 | +@SuppressWarnings({"unchecked", "rawtypes"}) |
| 70 | +public abstract class ReflectionUtils extends ReflectionUtilsPredicates { |
| 71 | + |
| 72 | + /** get type elements {@code <T>} by applying {@link QueryFunction} <pre>{@code get(SuperTypes.of(type))}</pre> */ |
| 73 | + public static <C, T> Set<T> get(QueryFunction<C, T> function) { |
| 74 | + return function.apply(null); |
| 75 | + } |
| 76 | + |
| 77 | + /** get type elements {@code <T>} by applying {@link QueryFunction} and {@code predicates} */ |
| 78 | + public static <T> Set<T> get(QueryFunction<Store, T> queryFunction, Predicate<? super T>... predicates) { |
| 79 | + return get(queryFunction.filter(Arrays.stream((Predicate[]) predicates).reduce(t -> true, Predicate::and))); |
| 80 | + } |
| 81 | + |
| 82 | + private static final List<String> objectMethodNames = |
| 83 | + Arrays.asList("equals", "hashCode", "toString", "wait", "notify", "notifyAll"); |
| 84 | + |
| 85 | + /** predicate to filter out {@code Object} methods */ |
| 86 | + public static final Predicate<Method> notObjectMethod = m -> !objectMethodNames.contains(m.getName()); |
| 87 | + |
| 88 | + /** query super class <pre>{@code get(SuperClass.of(element)) -> Set<Class<?>>}</pre> |
| 89 | + * <p>see also {@link ReflectionUtils#SuperTypes}, {@link ReflectionUtils#Interfaces} */ |
| 90 | + public static final UtilQueryBuilder<Class<?>, Class<?>> SuperClass = |
| 91 | + element -> ctx -> { |
| 92 | + Class<?> superclass = element.getSuperclass(); |
| 93 | + return superclass != null && !superclass.equals(Object.class) ? Collections.singleton(superclass) : Collections.emptySet(); |
| 94 | + }; |
| 95 | + |
| 96 | + /** query interfaces <pre>{@code get(Interfaces.of(element)) -> Set<Class<?>>}</pre> */ |
| 97 | + public static final UtilQueryBuilder<Class<?>, Class<?>> Interfaces = |
| 98 | + element -> ctx -> Stream.of(element.getInterfaces()).collect(Collectors.toCollection(LinkedHashSet::new)); |
| 99 | + |
| 100 | + /** query super classes and interfaces including element <pre>{@code get(SuperTypes.of(element)) -> Set<Class<?>> }</pre> */ |
| 101 | + public static final UtilQueryBuilder<Class<?>, Class<?>> SuperTypes = |
| 102 | + new UtilQueryBuilder<Class<?>, Class<?>>() { |
| 103 | + @Override |
| 104 | + public QueryFunction<Store, Class<?>> get(Class<?> element) { |
| 105 | + return SuperClass.get(element).add(Interfaces.get(element)); |
| 106 | + } |
| 107 | + |
| 108 | + @Override |
| 109 | + public QueryFunction<Store, Class<?>> of(Class<?> element) { |
| 110 | + return QueryFunction.<Store, Class<?>>single(element).getAll(SuperTypes::get); |
| 111 | + } |
| 112 | + }; |
| 113 | + |
| 114 | + /** query annotations <pre>{@code get(Annotation.of(element)) -> Set<Annotation> }</pre> */ |
| 115 | + public static final UtilQueryBuilder<AnnotatedElement, Annotation> Annotations = |
| 116 | + new UtilQueryBuilder<AnnotatedElement, Annotation>() { |
| 117 | + @Override |
| 118 | + public QueryFunction<Store, Annotation> get(AnnotatedElement element) { |
| 119 | + return ctx -> Arrays.stream(element.getAnnotations()).collect(Collectors.toCollection(LinkedHashSet::new)); |
| 120 | + } |
| 121 | + |
| 122 | + @Override |
| 123 | + public QueryFunction<Store, Annotation> of(AnnotatedElement element) { |
| 124 | + return ReflectionUtils.extendType().get(element).getAll(Annotations::get, Annotation::annotationType); |
| 125 | + } |
| 126 | + }; |
| 127 | + |
| 128 | + /** query annotation types <pre>{@code get(AnnotationTypes.of(element)) -> Set<Class<? extends Annotation>> }</pre> */ |
| 129 | + public static final UtilQueryBuilder<AnnotatedElement, Class<? extends Annotation>> AnnotationTypes = |
| 130 | + new UtilQueryBuilder<AnnotatedElement, Class<? extends Annotation>>() { |
| 131 | + @Override |
| 132 | + public QueryFunction<Store, Class<? extends Annotation>> get(AnnotatedElement element) { |
| 133 | + return Annotations.get(element).map(Annotation::annotationType); |
| 134 | + } |
| 135 | + |
| 136 | + @Override |
| 137 | + public QueryFunction<Store, Class<? extends Annotation>> of(AnnotatedElement element) { |
| 138 | + return ReflectionUtils.extendType().get(element).getAll(AnnotationTypes::get, a -> a); |
| 139 | + } |
| 140 | + }; |
| 141 | + |
| 142 | + /** query methods <pre>{@code get(Methods.of(type)) -> Set<Method>}</pre> */ |
| 143 | + public static final UtilQueryBuilder<Class<?>, Method> Methods = |
| 144 | + element -> ctx -> Arrays.stream(element.getDeclaredMethods()).filter(notObjectMethod).collect(Collectors.toCollection(LinkedHashSet::new)); |
| 145 | + |
| 146 | + /** query constructors <pre>{@code get(Constructors.of(type)) -> Set<Constructor> }</pre> */ |
| 147 | + public static final UtilQueryBuilder<Class<?>, Constructor> Constructors = |
| 148 | + element -> ctx -> Arrays.<Constructor>stream(element.getDeclaredConstructors()).collect(Collectors.toCollection(LinkedHashSet::new)); |
| 149 | + |
| 150 | + /** query fields <pre>{@code get(Fields.of(type)) -> Set<Field> }</pre> */ |
| 151 | + public static final UtilQueryBuilder<Class<?>, Field> Fields = |
| 152 | + element -> ctx -> Arrays.stream(element.getDeclaredFields()).collect(Collectors.toCollection(LinkedHashSet::new)); |
| 153 | + |
| 154 | + /** query url resources using {@link ClassLoader#getResources(String)} <pre>{@code get(Resources.with(name)) -> Set<URL> }</pre> */ |
| 155 | + public static final UtilQueryBuilder<String, URL> Resources = |
| 156 | + element -> ctx -> new HashSet<>(ClasspathHelper.forResource(element)); |
| 157 | + |
| 158 | + public static <T extends AnnotatedElement> UtilQueryBuilder<AnnotatedElement, T> extendType() { |
| 159 | + return element -> { |
| 160 | + if (element instanceof Class && !((Class<?>) element).isAnnotation()) { |
| 161 | + QueryFunction<Store, Class<?>> single = QueryFunction.single((Class<?>) element); |
| 162 | + return (QueryFunction<Store, T>) single.add(single.getAll(SuperTypes::get)); |
| 163 | + } else { |
| 164 | + return QueryFunction.single((T) element); |
| 165 | + } |
| 166 | + }; |
| 167 | + } |
| 168 | + |
| 169 | + /** get all annotations of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} |
| 170 | + * <p>marked for removal, use instead {@code get(Annotations.of())} */ |
| 171 | + public static <T extends AnnotatedElement> Set<Annotation> getAllAnnotations(T type, Predicate<Annotation>... predicates) { |
| 172 | + return get(Annotations.of(type), predicates); |
| 173 | + } |
| 174 | + |
| 175 | + /** get all super types of given {@code type}, including, optionally filtered by {@code predicates} */ |
| 176 | + public static Set<Class<?>> getAllSuperTypes(final Class<?> type, Predicate<? super Class<?>>... predicates) { |
| 177 | + Predicate<? super Class<?>>[] filter = predicates == null || predicates.length == 0 ? new Predicate[]{t -> !Object.class.equals(t)} : predicates; |
| 178 | + return get(SuperTypes.of(type), filter); |
| 179 | + } |
| 180 | + |
| 181 | + /** get the immediate supertype and interfaces of the given {@code type} |
| 182 | + * <p>marked for removal, use instead {@code get(SuperTypes.get())} */ |
| 183 | + public static Set<Class<?>> getSuperTypes(Class<?> type) { |
| 184 | + return get(SuperTypes.get(type)); |
| 185 | + } |
| 186 | + |
| 187 | + /** get all methods of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} |
| 188 | + * <p>marked for removal, use instead {@code get(Methods.of())} */ |
| 189 | + public static Set<Method> getAllMethods(final Class<?> type, Predicate<? super Method>... predicates) { |
| 190 | + return get(Methods.of(type), predicates); |
| 191 | + } |
| 192 | + |
| 193 | + /** get methods of given {@code type}, optionally filtered by {@code predicates} |
| 194 | + * <p>marked for removal, use instead {@code get(Methods.get())} */ |
| 195 | + public static Set<Method> getMethods(Class<?> t, Predicate<? super Method>... predicates) { |
| 196 | + return get(Methods.get(t), predicates); |
| 197 | + } |
| 198 | + |
| 199 | + /** get all constructors of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} |
| 200 | + * <p>marked for removal, use instead {@code get(Constructors.of())} */ |
| 201 | + public static Set<Constructor> getAllConstructors(final Class<?> type, Predicate<? super Constructor>... predicates) { |
| 202 | + return get(Constructors.of(type), predicates); |
| 203 | + } |
| 204 | + |
| 205 | + /** get constructors of given {@code type}, optionally filtered by {@code predicates} |
| 206 | + * <p>marked for removal, use instead {@code get(Constructors.get())} */ |
| 207 | + public static Set<Constructor> getConstructors(Class<?> t, Predicate<? super Constructor>... predicates) { |
| 208 | + return get(Constructors.get(t), predicates); |
| 209 | + } |
| 210 | + |
| 211 | + /** get all fields of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} |
| 212 | + * <p>marked for removal, use instead {@code get(Fields.of())} */ |
| 213 | + public static Set<Field> getAllFields(final Class<?> type, Predicate<? super Field>... predicates) { |
| 214 | + return get(Fields.of(type), predicates); |
| 215 | + } |
| 216 | + |
| 217 | + /** get fields of given {@code type}, optionally filtered by {@code predicates} |
| 218 | + * <p>marked for removal, use instead {@code get(Fields.get())} */ |
| 219 | + public static Set<Field> getFields(Class<?> type, Predicate<? super Field>... predicates) { |
| 220 | + return get(Fields.get(type), predicates); |
| 221 | + } |
| 222 | + |
| 223 | + /** get annotations of given {@code type}, optionally honorInherited, optionally filtered by {@code predicates} |
| 224 | + * <p>marked for removal, use instead {@code get(Annotations.get())} */ |
| 225 | + public static <T extends AnnotatedElement> Set<Annotation> getAnnotations(T type, Predicate<Annotation>... predicates) { |
| 226 | + return get(Annotations.get(type), predicates); |
| 227 | + } |
| 228 | + |
| 229 | + /** map {@code annotation} to hash map of member values recursively <pre>{@code Annotations.of(type).map(ReflectionUtils::toMap)} </pre>*/ |
| 230 | + public static Map<String, Object> toMap(Annotation annotation) { |
| 231 | + return get(Methods.of(annotation.annotationType()) |
| 232 | + .filter(notObjectMethod.and(withParametersCount(0)))) |
| 233 | + .stream() |
| 234 | + .collect(Collectors.toMap(Method::getName, m -> { |
| 235 | + Object v1 = invoke(m, annotation); |
| 236 | + return v1.getClass().isArray() && v1.getClass().getComponentType().isAnnotation() ? |
| 237 | + Stream.of((Annotation[]) v1).map(ReflectionUtils::toMap).collect(toList()) : v1; |
| 238 | + })); |
| 239 | + } |
| 240 | + |
| 241 | + /** map {@code annotation} and {@code annotatedElement} to hash map of member values |
| 242 | + * <pre>{@code Annotations.of(type).map(a -> toMap(type, a))} </pre>*/ |
| 243 | + public static Map<String, Object> toMap(Annotation annotation, AnnotatedElement element) { |
| 244 | + Map<String, Object> map = toMap(annotation); |
| 245 | + if (element != null) map.put("annotatedElement", element); |
| 246 | + return map; |
| 247 | + } |
| 248 | + |
| 249 | + /** create new annotation proxy with member values from the given {@code map} <pre>{@code toAnnotation(Map.of("annotationType", annotationType, "value", ""))}</pre> */ |
| 250 | + public static Annotation toAnnotation(Map<String, Object> map) { |
| 251 | + return toAnnotation(map, (Class<? extends Annotation>) map.get("annotationType")); |
| 252 | + } |
| 253 | + |
| 254 | + /** create new annotation proxy with member values from the given {@code map} and member values from the given {@code map} |
| 255 | + * <pre>{@code toAnnotation(Map.of("value", ""), annotationType)}</pre> */ |
| 256 | + public static <T extends Annotation> T toAnnotation(Map<String, Object> map, Class<T> annotationType) { |
| 257 | + return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class<?>[]{annotationType}, |
| 258 | + (proxy, method, args) -> notObjectMethod.test(method) ? map.get(method.getName()) : method.invoke(map)); |
| 259 | + } |
| 260 | + |
| 261 | + /** invoke the given {@code method} with {@code args}, return either the result or an exception if occurred */ |
| 262 | + public static Object invoke(Method method, Object obj, Object... args) { |
| 263 | + try { |
| 264 | + return method.invoke(obj, args); |
| 265 | + } catch (Exception e) { |
| 266 | + return e; |
| 267 | + } |
| 268 | + } |
| 269 | +} |
0 commit comments