package org.unbrokendome.eventbus.util;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public final class Reflection {
private static final int REFLECTION_CHARACTERISTICS =
Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.IMMUTABLE;
private Reflection() {
// prevent instantiation
}
public static Stream<Class<?>> typeAndAllSuperTypes(Class<?> clazz) {
if (clazz == null) {
return Stream.empty();
}
return Stream.concat(
Stream.of(clazz),
allSuperTypes(clazz));
}
public static Stream<Class<?>> allSuperTypes(Class<?> clazz) {
Stream<Class<?>> interfaces = StreamSupport.stream(
() -> Spliterators.spliterator(Arrays.asList(clazz.getInterfaces()), REFLECTION_CHARACTERISTICS),
REFLECTION_CHARACTERISTICS, true)
.flatMap(Reflection::typeAndAllSuperTypes);
return Stream.concat(typeAndAllSuperTypes(clazz.getSuperclass()), interfaces)
.distinct();
}
public static Stream<Method> allMethods(Class<?> clazz) {
return typeAndAllSuperTypes(clazz)
.flatMap(Reflection::getDeclaredMethods);
}
public static Stream<Method> allMethodsMatching(Class<?> clazz, MethodSignature signature) {
return typeAndAllSuperTypes(clazz)
.map(cls -> safeGetMethod(cls, signature))
.filter(Objects::nonNull);
}
private static Method safeGetMethod(Class<?> clazz, MethodSignature signature) {
try {
return clazz.getMethod(signature.getName(), signature.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
public static Stream<Method> getDeclaredMethods(Class<?> clazz) {
return StreamSupport.stream(
() -> Spliterators.spliterator(clazz.getDeclaredMethods(), REFLECTION_CHARACTERISTICS),
REFLECTION_CHARACTERISTICS, true);
}
public static Comparator<Method> methodOverridesComparator() {
return MethodOverridesComparator.INSTANCE;
}
private static class MethodOverridesComparator implements Comparator<Method> {
public static final MethodOverridesComparator INSTANCE = new MethodOverridesComparator();
private MethodOverridesComparator() { }
@Override
public int compare(Method method1, Method method2) {
if (method1 == null || method2 == null) {
throw new IllegalArgumentException("Argument must not be null");
}
if (method1 == method2) {
return 0;
}
if (!MethodSignature.of(method1).matches(method2)) {
throw new IllegalArgumentException("Method signatures do not match");
}
Class<?> declaringType1 = method1.getDeclaringClass();
Class<?> declaringType2 = method2.getDeclaringClass();
if (declaringType1.isAssignableFrom(declaringType2)) {
// method2 overrides method1
return -1;
} else if (declaringType2.isAssignableFrom(declaringType1)) {
// method1 overrides method2
return 1;
} else {
// neither method overrides the other; this might be the case if a class implements two
// interfaces that both declare the same method
return 0;
}
}
}
}