From 6d3f077c5da8630e3e508656ead1e705233a6a75 Mon Sep 17 00:00:00 2001 From: Tueem Date: Tue, 8 Apr 2025 01:56:46 +0200 Subject: [PATCH] migrate from marinara --- .vscode/settings.json | 2 +- gradle/libs.versions.toml | 2 + lib/build.gradle.kts | 2 +- .../net/tomatentum/cutin/MethodExecutor.java | 7 ++ .../net/tomatentum/cutin/MethodParser.java | 5 + .../net/tomatentum/cutin/MethodProcessor.java | 7 ++ .../tomatentum/cutin/ProcessorContainer.java | 7 ++ .../cutin/ProcessorMethodExecutor.java | 31 +++++ .../net/tomatentum/cutin/ReflectedMethod.java | 66 +++++++++++ .../cutin/ReflectedMethodFactory.java | 18 +++ .../cutin/ReflectedMethodFactoryImpl.java | 59 ++++++++++ .../tomatentum/cutin/util/ReflectionUtil.java | 107 ++++++++++++++++++ 12 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 lib/src/main/java/net/tomatentum/cutin/MethodExecutor.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/MethodParser.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/MethodProcessor.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/ProcessorContainer.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/ProcessorMethodExecutor.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/ReflectedMethod.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactory.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactoryImpl.java create mode 100644 lib/src/main/java/net/tomatentum/cutin/util/ReflectionUtil.java diff --git a/.vscode/settings.json b/.vscode/settings.json index 42ea84b..de2a843 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "java.configuration.updateBuildConfiguration": "interactive" + "java.configuration.updateBuildConfiguration": "automatic" } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5a98a3e..f7efc2c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,8 @@ [versions] junit-jupiter = "5.10.2" +slf4j = "2.0.17" [libraries] junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } +slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j"} diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 8abfe02..79d1f8d 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -20,7 +20,7 @@ dependencies { testImplementation(libs.junit.jupiter) testRuntimeOnly("org.junit.platform:junit-platform-launcher") - + implementation(libs.slf4j) } // Apply a specific Java toolchain to ease working on different environments. diff --git a/lib/src/main/java/net/tomatentum/cutin/MethodExecutor.java b/lib/src/main/java/net/tomatentum/cutin/MethodExecutor.java new file mode 100644 index 0000000..81bcd5e --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/MethodExecutor.java @@ -0,0 +1,7 @@ +package net.tomatentum.cutin; + +public interface MethodExecutor { + + void handle(Object context); + +} diff --git a/lib/src/main/java/net/tomatentum/cutin/MethodParser.java b/lib/src/main/java/net/tomatentum/cutin/MethodParser.java new file mode 100644 index 0000000..fc18a8d --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/MethodParser.java @@ -0,0 +1,5 @@ +package net.tomatentum.cutin; + +public interface MethodParser { + void parse(); +} diff --git a/lib/src/main/java/net/tomatentum/cutin/MethodProcessor.java b/lib/src/main/java/net/tomatentum/cutin/MethodProcessor.java new file mode 100644 index 0000000..cbbbcb0 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/MethodProcessor.java @@ -0,0 +1,7 @@ +package net.tomatentum.cutin; + +public interface MethodProcessor { + + void process(Object context); + +} diff --git a/lib/src/main/java/net/tomatentum/cutin/ProcessorContainer.java b/lib/src/main/java/net/tomatentum/cutin/ProcessorContainer.java new file mode 100644 index 0000000..da95b5b --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/ProcessorContainer.java @@ -0,0 +1,7 @@ +package net.tomatentum.cutin; + +public interface ProcessorContainer { + + ProcessorContainer addProcessor(MethodProcessor processor); + +} diff --git a/lib/src/main/java/net/tomatentum/cutin/ProcessorMethodExecutor.java b/lib/src/main/java/net/tomatentum/cutin/ProcessorMethodExecutor.java new file mode 100644 index 0000000..747609c --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/ProcessorMethodExecutor.java @@ -0,0 +1,31 @@ +package net.tomatentum.cutin; + +import java.util.HashSet; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ProcessorMethodExecutor implements MethodExecutor, ProcessorContainer { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + private Set processors; + + public ProcessorMethodExecutor() { + this.processors = new HashSet<>(); + } + + @Override + public ProcessorContainer addProcessor(MethodProcessor processor) { + processors.add(processor); + return this; + } + + @Override + public void handle(Object context) { + logger.debug("Received {} interaction ", context); + processors.forEach(x -> x.process(context)); + } + +} diff --git a/lib/src/main/java/net/tomatentum/cutin/ReflectedMethod.java b/lib/src/main/java/net/tomatentum/cutin/ReflectedMethod.java new file mode 100644 index 0000000..cf3e51f --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/ReflectedMethod.java @@ -0,0 +1,66 @@ +package net.tomatentum.cutin; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.tomatentum.cutin.util.ReflectionUtil; + +public abstract class ReflectedMethod { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + private Method method; + private Object containingObject; + + public ReflectedMethod(Method method, Object containingObject) { + if (!Arrays.asList(containingObject.getClass().getMethods()).contains(method)) + throw new InvalidParameterException("Method does not apply to specified handler"); + this.method = method; + this.containingObject = containingObject; + } + + public abstract Object getParameter(Object context, int index); + + public Object run(Object context) { + method.setAccessible(true); + try { + return method.invoke(containingObject, getParameters(context)); + }catch (IllegalAccessException | InvocationTargetException ex) { + logger.error("ReflectedMethod failed to run", ex); + return null; + } + } + + public Method method() { + return this.method; + } + + public Object containingObject() { + return this.containingObject; + } + + private Object[] getParameters(Object context) { + int parameterCount = method.getParameterCount(); + List parameters = new ArrayList<>(); + + for (int i = 0; i < parameterCount; i++) { + Object parameter; + if (i == 0) { + parameter = context; + }else + parameter = getParameter(context, i-1); + + logger.trace("Found parameter {}={} for method {}", parameter != null ? parameter.getClass().toString() : " ", parameter, ReflectionUtil.getFullMethodName(method)); + parameters.add(parameter); + } + return parameters.toArray(); + } + +} diff --git a/lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactory.java b/lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactory.java new file mode 100644 index 0000000..7f85a8d --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactory.java @@ -0,0 +1,18 @@ +package net.tomatentum.cutin; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; + + +public interface ReflectedMethodFactory { + Optional produce(Method method, Object containingClass); + ReflectedMethodFactory addFactory(Factory factory); + + public interface Factory { + + Optional produce(Method method, Object containingObject); + void addParser(ReflectedMethod method, List parser); + + } +} \ No newline at end of file diff --git a/lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactoryImpl.java b/lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactoryImpl.java new file mode 100644 index 0000000..7fa2517 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/ReflectedMethodFactoryImpl.java @@ -0,0 +1,59 @@ +package net.tomatentum.cutin; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.tomatentum.cutin.util.ReflectionUtil; + + +public class ReflectedMethodFactoryImpl implements ReflectedMethodFactory { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + private List factories; + + public ReflectedMethodFactoryImpl() { + this(new ArrayList<>()); + } + + public ReflectedMethodFactoryImpl(List factories) { + this.factories = factories; + } + + @Override + public Optional produce(Method method, Object containingClass) { + Optional imethod = this.factories.stream() + .map(f -> factoryProduce(f, method, containingClass)) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst(); + + if (imethod.isEmpty()) { + logger.debug("Could not produce a ReflectedMethod for Method {}", ReflectionUtil.getFullMethodName(method)); + } + + return imethod; + } + + @Override + public ReflectedMethodFactory addFactory(Factory factory) { + this.factories.add(factory); + return this; + } + + private Optional factoryProduce(Factory factory, Method method, Object containingClass) { + List parser = new ArrayList<>(); + Optional m = factory.produce(method, containingClass); + m.ifPresent(x -> { + factory.addParser(x, parser); + parser.forEach(MethodParser::parse); + }); + return m; + } + +} diff --git a/lib/src/main/java/net/tomatentum/cutin/util/ReflectionUtil.java b/lib/src/main/java/net/tomatentum/cutin/util/ReflectionUtil.java new file mode 100644 index 0000000..0e0d453 --- /dev/null +++ b/lib/src/main/java/net/tomatentum/cutin/util/ReflectionUtil.java @@ -0,0 +1,107 @@ +package net.tomatentum.cutin.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(); + } +}