diff --git a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java index 0c7f96e..4f80cff 100644 --- a/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java +++ b/lib/src/main/java/net/tomatentum/marinara/util/ReflectionUtil.java @@ -2,6 +2,10 @@ 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 { @@ -17,5 +21,79 @@ public final class ReflectionUtil { method.getAnnotation(annotationClass) : method.getDeclaringClass().getAnnotation(annotationClass); } + + public static int getCastDepth(Class child, Class parent) { + 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; + } }