migrate from marinara
This commit is contained in:
parent
4036112736
commit
6d3f077c5d
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -1,3 +1,3 @@
|
||||
{
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
"java.configuration.updateBuildConfiguration": "automatic"
|
||||
}
|
@ -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"}
|
||||
|
@ -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.
|
||||
|
@ -0,0 +1,7 @@
|
||||
package net.tomatentum.cutin;
|
||||
|
||||
public interface MethodExecutor {
|
||||
|
||||
void handle(Object context);
|
||||
|
||||
}
|
5
lib/src/main/java/net/tomatentum/cutin/MethodParser.java
Normal file
5
lib/src/main/java/net/tomatentum/cutin/MethodParser.java
Normal file
@ -0,0 +1,5 @@
|
||||
package net.tomatentum.cutin;
|
||||
|
||||
public interface MethodParser {
|
||||
void parse();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package net.tomatentum.cutin;
|
||||
|
||||
public interface MethodProcessor {
|
||||
|
||||
void process(Object context);
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package net.tomatentum.cutin;
|
||||
|
||||
public interface ProcessorContainer {
|
||||
|
||||
ProcessorContainer addProcessor(MethodProcessor processor);
|
||||
|
||||
}
|
@ -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<MethodProcessor> 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));
|
||||
}
|
||||
|
||||
}
|
66
lib/src/main/java/net/tomatentum/cutin/ReflectedMethod.java
Normal file
66
lib/src/main/java/net/tomatentum/cutin/ReflectedMethod.java
Normal file
@ -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<Object> 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();
|
||||
}
|
||||
|
||||
}
|
@ -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<ReflectedMethod> produce(Method method, Object containingClass);
|
||||
ReflectedMethodFactory addFactory(Factory factory);
|
||||
|
||||
public interface Factory {
|
||||
|
||||
Optional<ReflectedMethod> produce(Method method, Object containingObject);
|
||||
void addParser(ReflectedMethod method, List<MethodParser> parser);
|
||||
|
||||
}
|
||||
}
|
@ -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<Factory> factories;
|
||||
|
||||
public ReflectedMethodFactoryImpl() {
|
||||
this(new ArrayList<>());
|
||||
}
|
||||
|
||||
public ReflectedMethodFactoryImpl(List<Factory> factories) {
|
||||
this.factories = factories;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ReflectedMethod> produce(Method method, Object containingClass) {
|
||||
Optional<ReflectedMethod> 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<ReflectedMethod> factoryProduce(Factory factory, Method method, Object containingClass) {
|
||||
List<MethodParser> parser = new ArrayList<>();
|
||||
Optional<ReflectedMethod> m = factory.produce(method, containingClass);
|
||||
m.ifPresent(x -> {
|
||||
factory.addParser(x, parser);
|
||||
parser.forEach(MethodParser::parse);
|
||||
});
|
||||
return m;
|
||||
}
|
||||
|
||||
}
|
107
lib/src/main/java/net/tomatentum/cutin/util/ReflectionUtil.java
Normal file
107
lib/src/main/java/net/tomatentum/cutin/util/ReflectionUtil.java
Normal file
@ -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 <T extends Annotation> boolean isAnnotationPresent(Method method, Class<T> annotationClass) {
|
||||
if (method.isAnnotationPresent(annotationClass) || method.getDeclaringClass().isAnnotationPresent(annotationClass))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T extends Annotation> T getAnnotation(Method method, Class<T> 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<Class<?>> 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<Method> 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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user