package net.tomatentum.marinara.util; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; public final class ReflectionUtil { public static boolean isAnnotationPresent(Method method, Class annotationClass) { if (method.isAnnotationPresent(annotationClass) || method.getDeclaringClass().isAnnotationPresent(annotationClass)) return true; return false; } public static T getAnnotation(Method method, Class annotationClass) { return method.isAnnotationPresent(annotationClass) ? method.getAnnotation(annotationClass) : method.getDeclaringClass().getAnnotation(annotationClass); } public static int getCastDepth(Class child, Class parent) { if (parent.equals(Object.class)) return Integer.MAX_VALUE; if (!parent.isAssignableFrom(child)) { throw new IllegalArgumentException("The specified class is not a child class of the specified parent."); } int depth = 0; Class curr = child; List> parents = new ArrayList<>(); while (!curr.equals(parent)) { depth++; parents.add(curr.getSuperclass()); parents.addAll(Arrays.asList(curr.getInterfaces())); for (Class currParent : parents) { if (currParent != null && parent.isAssignableFrom(currParent)) { curr = currParent; break; } } parents.clear(); } return depth; } public static Method getMostSpecificMethod(Method[] methods, Class... parameters) { List compatibleMethods = Arrays.stream(methods) .filter(x -> isMethodCallable(x, parameters)) .toList(); if (compatibleMethods.size() == 0) throw new IllegalArgumentException("There are no compatible Methods provided"); for (int i = 0; i < parameters.length; i++) { final int currI = i; Class[] parameterTypes = compatibleMethods.stream() .map(x -> x.getParameterTypes()[currI]) .toArray(x -> new Class[x]); Class mostSpecific = getMostSpecificClass(parameterTypes, parameters[i]); compatibleMethods = compatibleMethods.stream() .filter(x -> Objects.equals(x.getParameterTypes()[currI], mostSpecific)) .toList(); } return compatibleMethods.getFirst(); } public static Class getMostSpecificClass(Class[] classes, Class base) { int min = Integer.MAX_VALUE; Class currMostSpecific = null; for (Class currClass : classes) { int currCastDepth = getCastDepth(base, currClass); if (currCastDepth <= min) { min = currCastDepth; currMostSpecific = currClass; } } return currMostSpecific; } public static boolean isMethodCallable(Method method, Class... parameters) { if (!Objects.equals(method.getParameterCount(), parameters.length)) return false; Class[] methodParams = method.getParameterTypes(); for (int i = 0; i < parameters.length; i++) { if (!methodParams[i].isAssignableFrom(parameters[i])) return false; } return true; } public static String getFullMethodName(Method method) { return method.getDeclaringClass().getName() + "." + method.getName(); } }