Compare commits

..

No commits in common. "master" and "v0.1.0" have entirely different histories.

21 changed files with 169 additions and 324 deletions

View File

@ -6,7 +6,7 @@ plugins {
allprojects { allprojects {
group = "net.tomatentum.cutin" group = "net.tomatentum.cutin"
version = "0.2.0" + (if (!project.hasProperty("release")) ("-" + getGitHash()) else "") version = "0.1.0" + (if (!project.hasProperty("release")) ("-" + getGitHash()) else "")
description = "A lightweight Reflection abstraction specifically but not exclusively made for tueem/Marinara." description = "A lightweight Reflection abstraction specifically but not exclusively made for tueem/Marinara."
plugins.withType<JavaPlugin> { plugins.withType<JavaPlugin> {
tasks.withType<Jar>().configureEach { tasks.withType<Jar>().configureEach {

View File

@ -8,4 +8,3 @@ slf4j = "2.0.17"
[libraries] [libraries]
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j"} slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j"}
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j"}

View File

@ -20,8 +20,6 @@ dependencies {
testImplementation(libs.junit.jupiter) testImplementation(libs.junit.jupiter)
testRuntimeOnly("org.junit.platform:junit-platform-launcher") testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation(libs.slf4j.simple)
implementation(libs.slf4j) implementation(libs.slf4j)
} }

View File

@ -0,0 +1,53 @@
package net.tomatentum.cutin;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import net.tomatentum.cutin.util.ReflectionUtil;
public class BestCandidateMethod<I> extends ReflectedMethod<I> {
private String methodName;
private I identifier;
private List<Object> additionalParameters;
protected BestCandidateMethod(String methodName, Object containingObject, I identifer, Object... additionalParameters) {
super(getMethod(containingObject, methodName), containingObject);
this.methodName = methodName;
this.identifier = identifer;
this.additionalParameters = Arrays.asList(additionalParameters);
}
@Override
public Object getParameter(Object context, int index) {
return additionalParameters.get(index);
}
@Override
public I identifier() {
return this.identifier;
}
@Override
public Object run(Object context) {
Method[] methods = Arrays.stream(containingObject.getClass().getDeclaredMethods())
.filter(x -> x.getName().equals(methodName))
.filter(x -> !x.isBridge())
.toArray(Method[]::new);
Class<?>[] parameters = Stream.concat(
Stream.of(context.getClass()),
additionalParameters.stream().map(Object::getClass)
).toArray(Class<?>[]::new);
super.method = ReflectionUtil.getMostSpecificMethod(methods, parameters);
return super.run(context);
}
private static Method getMethod(Object containingMethod, String methodName) {
return Arrays.stream(containingMethod.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName))
.findFirst().orElse(null);
}
}

View File

@ -1,7 +1,7 @@
package net.tomatentum.cutin; package net.tomatentum.cutin;
public interface MethodExecutor<C extends Object> { public interface MethodExecutor {
void handle(C context); void handle(Object context);
} }

View File

@ -1,7 +1,5 @@
package net.tomatentum.cutin; package net.tomatentum.cutin;
import java.lang.reflect.Method;
public interface MethodParser { public interface MethodParser {
Object parse(Method method, Object containingObject); void parse();
} }

View File

@ -2,8 +2,8 @@ package net.tomatentum.cutin;
import net.tomatentum.cutin.container.MethodContainer; import net.tomatentum.cutin.container.MethodContainer;
public interface MethodProcessor<I extends Object, C extends Object> { public interface MethodProcessor<I extends Object> {
void process(Object context, MethodContainer<I, C> methodContainer); void process(Object context, MethodContainer<I> methodContainer);
} }

View File

@ -2,10 +2,10 @@ package net.tomatentum.cutin;
import java.util.Collection; import java.util.Collection;
public interface ProcessorContainer<I extends Object, C extends Object> { public interface ProcessorContainer<I> {
ProcessorContainer<I, C> addProcessor(MethodProcessor<I, C> processor); ProcessorContainer<I> addProcessor(MethodProcessor<I> processor);
Collection<MethodProcessor<I, C>> processor(); Collection<MethodProcessor<I>> processor();
} }

View File

@ -9,26 +9,26 @@ import org.slf4j.LoggerFactory;
import net.tomatentum.cutin.container.MethodContainer; import net.tomatentum.cutin.container.MethodContainer;
public class ProcessorMethodExecutor<I extends Object, C extends Object> implements MethodExecutor<C>, ProcessorContainer<I, C> { public class ProcessorMethodExecutor<I extends Object> implements MethodExecutor, ProcessorContainer<I> {
private Logger logger = LoggerFactory.getLogger(getClass()); private Logger logger = LoggerFactory.getLogger(getClass());
private MethodContainer<I, C> methodContainer; private MethodContainer<I> methodContainer;
private Set<MethodProcessor<I, C>> processors; private Set<MethodProcessor<I>> processors;
public ProcessorMethodExecutor(MethodContainer<I, C> methodContainer) { public ProcessorMethodExecutor(MethodContainer<I> methodContainer) {
this.methodContainer = methodContainer; this.methodContainer = methodContainer;
this.processors = new HashSet<>(); this.processors = new HashSet<>();
} }
@Override @Override
public ProcessorContainer<I, C> addProcessor(MethodProcessor<I, C> processor) { public ProcessorContainer<I> addProcessor(MethodProcessor<I> processor) {
processors.add(processor); processors.add(processor);
return this; return this;
} }
@Override @Override
public Collection<MethodProcessor<I, C>> processor() { public Collection<MethodProcessor<I>> processor() {
return this.processors; return this.processors;
} }

View File

@ -1,4 +1,4 @@
package net.tomatentum.cutin.method; package net.tomatentum.cutin;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -12,7 +12,7 @@ import org.slf4j.LoggerFactory;
import net.tomatentum.cutin.util.ReflectionUtil; import net.tomatentum.cutin.util.ReflectionUtil;
public abstract class ReflectedMethod<I extends Object, C extends Object> { public abstract class ReflectedMethod<I extends Object> {
private Logger logger = LoggerFactory.getLogger(getClass()); private Logger logger = LoggerFactory.getLogger(getClass());
@ -26,17 +26,16 @@ public abstract class ReflectedMethod<I extends Object, C extends Object> {
this.containingObject = containingObject; this.containingObject = containingObject;
} }
public abstract Object getParameter(C context, int index); public abstract Object getParameter(Object context, int index);
public abstract I identifier(); public abstract I identifier();
public Object run(C context) { public Object run(Object context) {
method.setAccessible(true); method.setAccessible(true);
try { try {
logger.debug("Invoking method {} from {}", ReflectionUtil.getFullMethodName(method), this);
return method.invoke(containingObject, getParameters(context)); return method.invoke(containingObject, getParameters(context));
}catch (IllegalAccessException | InvocationTargetException ex) { }catch (IllegalAccessException | InvocationTargetException ex) {
logger.error("ReflectedMethod %s failed to run".formatted(this), ex); logger.error("ReflectedMethod failed to run", ex);
return null; return null;
} }
} }
@ -49,18 +48,18 @@ public abstract class ReflectedMethod<I extends Object, C extends Object> {
return this.containingObject; return this.containingObject;
} }
@Override private Object[] getParameters(Object context) {
public String toString() {
return "ReflectedMethod(%s)".formatted(identifier());
}
private Object[] getParameters(C context) {
int parameterCount = method.getParameterCount(); int parameterCount = method.getParameterCount();
List<Object> parameters = new ArrayList<>(); List<Object> parameters = new ArrayList<>();
for (int i = 0; i < parameterCount; i++) { for (int i = 0; i < parameterCount; i++) {
Object parameter = getParameter(context, i); Object parameter;
logger.trace("Found parameter {}={} for method {} in {}", parameter != null ? parameter.getClass().toString() : " ", parameter, ReflectionUtil.getFullMethodName(method), this); 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); parameters.add(parameter);
} }
return parameters.toArray(); return parameters.toArray();

View File

@ -1,40 +1,18 @@
package net.tomatentum.cutin; package net.tomatentum.cutin;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import net.tomatentum.cutin.method.ReflectedMethod;
public interface ReflectedMethodFactory<I extends Object, C extends Object> { public interface ReflectedMethodFactory {
Optional<ReflectedMethod<I, C>> produce(Method method, Object containingClass); Optional<ReflectedMethod> produce(Method method, Object containingClass);
ReflectedMethodFactory<I, C> addFactory(Factory<I, C> factory); ReflectedMethodFactory addFactory(Factory factory);
public interface Factory<I extends Object, C extends Object> { public interface Factory {
Optional<ReflectedMethod<I, C>> produce(Method method, Object containingObject, ParserResults parserResults); Optional<ReflectedMethod> produce(Method method, Object containingObject);
void addParser(Set<MethodParser> parser); void addParser(ReflectedMethod method, List<MethodParser> parser);
}
public static class ParserResults extends HashMap<Class<? extends MethodParser>, Object> {
public static ParserResults create(Set<MethodParser> parser, Method method, Object containingObject) {
ParserResults results = new ParserResults();
for (MethodParser p : parser) {
results.put(p.getClass(), p.parse(method, containingObject));
}
return results;
}
private ParserResults() {}
@SuppressWarnings("unchecked")
public <T extends Object> T get(Class<? extends MethodParser> key) {
return (T) super.get(key);
}
} }
} }

View File

@ -2,60 +2,58 @@ package net.tomatentum.cutin;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import net.tomatentum.cutin.method.ReflectedMethod;
import net.tomatentum.cutin.util.ReflectionUtil; import net.tomatentum.cutin.util.ReflectionUtil;
public class ReflectedMethodFactoryImpl<I extends Object, C extends Object> implements ReflectedMethodFactory<I, C> { public class ReflectedMethodFactoryImpl implements ReflectedMethodFactory {
private Logger logger = LoggerFactory.getLogger(getClass()); private Logger logger = LoggerFactory.getLogger(getClass());
private List<Factory<I, C>> factories; private List<Factory> factories;
public ReflectedMethodFactoryImpl() { public ReflectedMethodFactoryImpl() {
this(new ArrayList<>()); this(new ArrayList<>());
} }
public ReflectedMethodFactoryImpl(List<Factory<I, C>> factories) { public ReflectedMethodFactoryImpl(List<Factory> factories) {
this.factories = factories; this.factories = factories;
} }
@Override @Override
public Optional<ReflectedMethod<I, C>> produce(Method method, Object containingClass) { public Optional<ReflectedMethod> produce(Method method, Object containingClass) {
Optional<ReflectedMethod<I, C>> rmethod = this.factories.stream() Optional<ReflectedMethod> rmethod = this.factories.stream()
.map(f -> factoryProduce(f, method, containingClass)) .map(f -> factoryProduce(f, method, containingClass))
.filter(Optional::isPresent) .filter(Optional::isPresent)
.map(Optional::get) .map(Optional::get)
.findFirst(); .findFirst();
if (rmethod.isEmpty()) if (rmethod.isEmpty()) {
logger.warn("Could not produce a ReflectedMethod for Method {} in {}", ReflectionUtil.getFullMethodName(method), this); logger.debug("Could not produce a ReflectedMethod for Method {}", ReflectionUtil.getFullMethodName(method));
else }
logger.debug("Produced {} for Method {} in {}", rmethod.get(), ReflectionUtil.getFullMethodName(method), this);
return rmethod; return rmethod;
} }
@Override @Override
public ReflectedMethodFactory<I, C> addFactory(Factory<I, C> factory) { public ReflectedMethodFactory addFactory(Factory factory) {
this.factories.add(factory); this.factories.add(factory);
logger.trace("Added Factory {} to {}", factory, this);
return this; return this;
} }
private Optional<ReflectedMethod<I, C>> factoryProduce(Factory<I, C> factory, Method method, Object containingClass) { private Optional<ReflectedMethod> factoryProduce(Factory factory, Method method, Object containingClass) {
Set<MethodParser> parser = new HashSet<>(); List<MethodParser> parser = new ArrayList<>();
factory.addParser(parser); Optional<ReflectedMethod> m = factory.produce(method, containingClass);
ParserResults results = ParserResults.create(parser, method, containingClass); m.ifPresent(x -> {
return factory.produce(method, containingClass, results); factory.addParser(x, parser);
parser.forEach(MethodParser::parse);
});
return m;
} }
} }

View File

@ -3,68 +3,48 @@ package net.tomatentum.cutin.container;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.tomatentum.cutin.ReflectedMethod;
import net.tomatentum.cutin.ReflectedMethodFactory; import net.tomatentum.cutin.ReflectedMethodFactory;
import net.tomatentum.cutin.method.ReflectedMethod;
public class LoneMethodContainer<I extends Object, C extends Object> implements MethodContainer<I, C> { public class LoneMethodContainer<I extends Object> implements MethodContainer<I> {
private Logger logger = LoggerFactory.getLogger(getClass()); private Map<I, ReflectedMethod<I>> methodStore;
private ReflectedMethodFactory factory;
private Map<I, ReflectedMethod<I, C>> methodStore; public LoneMethodContainer(ReflectedMethodFactory factory) {
private ReflectedMethodFactory<I, C> factory;
public LoneMethodContainer(ReflectedMethodFactory<I, C> factory) {
this.methodStore = new HashMap<>(); this.methodStore = new HashMap<>();
this.factory = factory; this.factory = factory;
} }
@Override @Override
public MethodContainer<I, C> addMethod(ReflectedMethod<I, C> method) { public void addMethods(ReflectedMethod<I>... methods) {
this.methodStore.put(method.identifier(), method); for (ReflectedMethod<I> reflectedMethod : methods)
logger.debug("Added {} to container", method); this.methodStore.put(reflectedMethod.identifier(), reflectedMethod);
return this;
} }
@Override @Override
public MethodContainer<I, C> addMethods(Object containingObject, Method... methods) { public void addMethods(Object containingObject, Method... methods) {
for (Method method : methods) for (Method method : methods)
this.factory.produce(method, containingObject) this.addMethods(this.factory.produce(method, containingObject));
.ifPresent(this::addMethod);
return this;
} }
@Override @Override
public Set<I> identifiers() { public Collection<ReflectedMethod<I>> methods() {
return methodStore.keySet();
}
@Override
public Collection<ReflectedMethod<I, C>> methods() {
return this.methodStore.values(); return this.methodStore.values();
} }
@Override @Override
public Collection<ReflectedMethod<I, C>> findFor(I identifier) { public Collection<ReflectedMethod<I>> findFor(I identifier) {
ReflectedMethod<I, C> result = this.methodStore.get(identifier); return Arrays.asList(this.methodStore.get(identifier));
return result != null ? Arrays.asList(result) : Collections.emptyList();
} }
@Override @Override
public Optional<ReflectedMethod<I, C>> findFirstFor(I identifier) { public Optional<ReflectedMethod<I>> findFirstFor(I identifier) {
return findFor(identifier).stream().findFirst(); return findFor(identifier).stream().findFirst();
} }
public ReflectedMethodFactory<I, C> factory() {
return this.factory;
}
} }

View File

@ -3,28 +3,19 @@ package net.tomatentum.cutin.container;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import net.tomatentum.cutin.method.ReflectedMethod; import net.tomatentum.cutin.ReflectedMethod;
public interface MethodContainer<I extends Object, C extends Object> { public interface MethodContainer<I extends Object> {
MethodContainer<I, C> addMethod(ReflectedMethod<I, C> method); void addMethods(ReflectedMethod<I>... methods);
default MethodContainer<I, C> addMethods(ReflectedMethod<I, C>[] methods) { void addMethods(Object containingObject, Method... methods);
for (ReflectedMethod<I, C> reflectedMethod : methods) { default void addAllMethods(Object containingObject) {
this.addMethod(reflectedMethod);
}
return this;
}
MethodContainer<I, C> addMethods(Object containingObject, Method... methods);
default MethodContainer<I, C> addAllMethods(Object containingObject) {
this.addMethods(containingObject, containingObject.getClass().getDeclaredMethods()); this.addMethods(containingObject, containingObject.getClass().getDeclaredMethods());
return this; };
}
Set<I> identifiers(); Collection<ReflectedMethod<I>> methods();
Collection<ReflectedMethod<I, C>> methods(); Collection<ReflectedMethod<I>> findFor(I identifier);
Collection<ReflectedMethod<I, C>> findFor(I identifier); Optional<ReflectedMethod<I>> findFirstFor(I identifier);
Optional<ReflectedMethod<I, C>> findFirstFor(I identifier);
} }

View File

@ -5,59 +5,50 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import net.tomatentum.cutin.ReflectedMethod;
import net.tomatentum.cutin.ReflectedMethodFactory; import net.tomatentum.cutin.ReflectedMethodFactory;
import net.tomatentum.cutin.method.ReflectedMethod;
import net.tomatentum.cutin.util.ReflectionUtil; import net.tomatentum.cutin.util.ReflectionUtil;
public class MultiMethodContainer<I extends Object, C extends Object> implements MethodContainer<I, C> { public class MultiMethodContainer<I extends Object> implements MethodContainer<I> {
private Set<Entry<I, C>> entries; private Set<Entry<I>> entries;
private ReflectedMethodFactory<I, C> factory; private ReflectedMethodFactory factory;
public MultiMethodContainer(ReflectedMethodFactory<I, C> factory) { public MultiMethodContainer(ReflectedMethodFactory factory) {
this.entries = new HashSet<>(); this.entries = new HashSet<>();
this.factory = factory; this.factory = factory;
} }
@Override @Override
public MethodContainer<I, C> addMethod(ReflectedMethod<I, C> method) { public void addMethods(ReflectedMethod<I>... methods) {
Optional<Entry<I, C>> oentry = this.entries.stream() for (ReflectedMethod<I> rMethod : methods) {
.filter(e -> method.identifier().equals(e.identifier())) Optional<Entry<I>> oentry = this.entries.stream()
.filter(e -> rMethod.identifier().equals(e.identifier()))
.findFirst(); .findFirst();
Entry<I, C> entry = oentry.orElse(new Entry<>(method.identifier())).addMethod(method); Entry<I> entry = oentry.orElse(new Entry<>(rMethod.identifier())).addMethod(rMethod);
if (oentry.isEmpty()) this.entries.add(entry); if (oentry.isEmpty()) this.entries.add(entry);
return this; }
} }
@Override @Override
public MethodContainer<I, C> addMethods(Object containingObject, Method... methods) { public void addMethods(Object containingObject, Method... methods) {
for (Method method : methods) for (Method method : methods)
this.factory.produce(method, containingObject) this.addMethods(this.factory.produce(method, containingObject));
.ifPresent(this::addMethod);
return this;
} }
@Override @Override
public Set<I> identifiers() { public Collection<ReflectedMethod<I>> methods() {
return entries().stream()
.map(Entry::identifier)
.collect(Collectors.toSet());
}
@Override
public Collection<ReflectedMethod<I, C>> methods() {
return this.entries.stream() return this.entries.stream()
.flatMap(e -> e.methods.stream()) .flatMap(e -> e.methods.stream())
.toList(); .toList();
} }
@Override @Override
public Collection<ReflectedMethod<I, C>> findFor(I identifier) { public Collection<ReflectedMethod<I>> findFor(I identifier) {
return this.entries.stream() return this.entries.stream()
.filter(e -> e.identifier().equals(identifier)) .filter(e -> e.identifier().equals(identifier))
.flatMap(e -> e.methods.stream()) .flatMap(e -> e.methods.stream())
@ -65,22 +56,14 @@ public class MultiMethodContainer<I extends Object, C extends Object> implements
} }
@Override @Override
public Optional<ReflectedMethod<I, C>> findFirstFor(I identifier) { public Optional<ReflectedMethod<I>> findFirstFor(I identifier) {
return this.entries.stream() return this.entries.stream()
.filter(e -> e.identifier().equals(identifier)) .filter(e -> e.identifier().equals(identifier))
.flatMap(e -> e.methods.stream()) .flatMap(e -> e.methods.stream())
.findFirst(); .findFirst();
} }
protected Set<Entry<I, C>> entries() { public static record Entry<I extends Object>(I identifier, Set<ReflectedMethod<I>> methods) {
return this.entries;
}
public ReflectedMethodFactory<I, C> factory() {
return this.factory;
}
public static record Entry<I extends Object, C extends Object>(I identifier, Set<ReflectedMethod<I, C>> methods) {
public Entry(I identifier) { public Entry(I identifier) {
this(identifier, new HashSet<>()); this(identifier, new HashSet<>());
@ -88,22 +71,33 @@ public class MultiMethodContainer<I extends Object, C extends Object> implements
private static Logger logger = LoggerFactory.getLogger(Entry.class); private static Logger logger = LoggerFactory.getLogger(Entry.class);
public Entry<I, C> addMethod(ReflectedMethod<I, C> method) { public Entry<I> addMethod(ReflectedMethod<I> method) {
I midentifier = method.identifier(); I midentifier = method.identifier();
if (!this.identifier().equals(midentifier)) if (!this.identifier().equals(midentifier))
throw new IllegalArgumentException("Method's identifier did not equal the entry's identifier"); throw new IllegalArgumentException("Method's identifier did not equal the entry's identifier");
this.methods.add(method); this.methods.add(method);
logger.debug("Added method {} to entry {}", ReflectionUtil.getFullMethodName(method.method()), this); logger.debug("Added method {} to entry {}", method.method().getName(), this);
return this; return this;
} }
public Object[] runAll(Object context) {
logger.trace("Running all Methods from {} with context {}", this, context);
return this.methods.stream()
.map(x -> {
logger.debug("Running Method {} from {} with context {}", x, this, context);
return x.run(context);
})
.flatMap(ReflectionUtil::getReturnAsStream)
.toArray();
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (!(obj instanceof Entry)) if (!(obj instanceof Entry))
return false; return false;
Entry<?, ?> other = (Entry<?, ?>) obj; Entry<?> other = (Entry<?>) obj;
return other.identifier().equals(identifier()); return other.identifier().equals(identifier());
} }
@ -114,7 +108,7 @@ public class MultiMethodContainer<I extends Object, C extends Object> implements
@Override @Override
public String toString() { public String toString() {
return "Ident(%s)".formatted(identifier().toString()); return "Content(%s)".formatted(identifier().toString());
} }
} }

View File

@ -1,96 +0,0 @@
package net.tomatentum.cutin.method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.tomatentum.cutin.ReflectedMethodFactory;
import net.tomatentum.cutin.ReflectedMethodFactory.ParserResults;
import net.tomatentum.cutin.container.MethodContainer;
import net.tomatentum.cutin.util.ReflectionUtil;
public abstract class BestCandidateMethod<I extends Object, C extends Object> extends ReflectedMethod<I, C> {
private Logger logger = LoggerFactory.getLogger(getClass());
private String methodName;
protected BestCandidateMethod(String methodName, Object containingObject) {
super(getMethod(containingObject, methodName), containingObject);
this.methodName = methodName;
}
@Override
public Object run(C context) {
Method[] methods = Arrays.stream(containingObject.getClass().getDeclaredMethods())
.filter(x -> x.getName().equals(methodName))
.filter(x -> !x.isBridge())
.toArray(Method[]::new);
Class<?>[] parameters = getCurrentParameterList(context).stream()
.map(Object::getClass)
.toArray(Class<?>[]::new);
super.method = ReflectionUtil.getMostSpecificMethod(methods, parameters);
logger.trace("Found {} for {}({}) in {}", ReflectionUtil.getFullMethodName(method()), this.methodName, String.join(", ", Arrays.stream(parameters).map(Object::toString).toList()), this);
return super.run(context);
}
private static Method getMethod(Object containingMethod, String methodName) {
return Arrays.stream(containingMethod.getClass().getDeclaredMethods())
.filter(m -> m.getName().equals(methodName))
.findFirst().orElse(null);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BestCandidateMethod<?, ?> bcMethod) {
return this.containingObject().getClass().equals(bcMethod.containingObject().getClass()) &&
this.methodName.equals(bcMethod.methodName);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(containingObject.getClass(), methodName);
}
private List<Object> getCurrentParameterList(C context) {
List<Object> parameters = new ArrayList<>();
int c = 0;
Object last;
while ((last = getParameter(context, c)) != null) {
parameters.add(last);
c++;
}
return parameters;
}
public abstract static class Factory<I extends Object, C extends Object> implements ReflectedMethodFactory.Factory<I, C> {
private MethodContainer<I, C> methodContainer;
private String methodName;
protected Factory(MethodContainer<I, C> methodContainer, String methodName) {
this.methodContainer = methodContainer;
this.methodName = methodName;
}
@Override
public Optional<ReflectedMethod<I, C>> produce(Method method, Object containingObject, ParserResults results) {
Optional<BestCandidateMethod<I, C>> bcMethod = bcProduce(methodName, containingObject, results);
if (bcMethod.isEmpty() || methodContainer.methods().contains(bcMethod.get()))
return Optional.empty();
return Optional.of(bcMethod.get());
}
protected abstract Optional<BestCandidateMethod<I, C>> bcProduce(String methodName, Object containingObject, ParserResults results);
}
}

View File

@ -135,7 +135,6 @@ public final class ReflectionUtil {
} }
public static String getFullMethodName(Method method) { public static String getFullMethodName(Method method) {
List<String> parameters = Arrays.stream(method.getParameterTypes()).map(Object::toString).toList(); return method.getDeclaringClass().getName() + "." + method.getName();
return String.format("%s.%s(%s)", method.getDeclaringClass().getName(), method.getName(), String.join(", ", parameters));
} }
} }

View File

@ -4,25 +4,23 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import net.tomatentum.cutin.method.ReflectedMethod;
class ReflectedMethodTest { class ReflectedMethodTest {
@Test @Test
void methodTest() { void methodTest() {
ReflectedMethod<String, String> method = new TestReflectedMethod(new TestMethodClass()); ReflectedMethod<String> method = new TestReflectedMethod(new TestMethodClass());
Object result = method.run("testContext"); Object result = method.run("testContext");
assertTrue((boolean)result); assertTrue((boolean)result);
System.out.println("Success");
} }
@Test @Test
void testBCMethod() { void testBCMethod() {
ReflectedMethod<String, Double> method = new TestBestCandidateMethod( ReflectedMethod<?> method = new BestCandidateMethod<String>(
"test", "test",
new TestMethodClass()); new TestMethodClass(),
"ident",
"testString");
Object result = method.run((double)4); Object result = method.run((double)4);
assertTrue((boolean)result); assertTrue((boolean)result);
System.out.println("Success");
} }
} }

View File

@ -1,28 +0,0 @@
package net.tomatentum.cutin;
import net.tomatentum.cutin.method.BestCandidateMethod;
public class TestBestCandidateMethod extends BestCandidateMethod<String, Double> {
protected TestBestCandidateMethod(String methodName, Object containingObject) {
super(methodName, containingObject);
}
@Override
public Object getParameter(Double context, int index) {
switch (index) {
case 0:
return context;
case 1:
return "testString";
default:
return null;
}
}
@Override
public String identifier() {
return "ident";
}
}

View File

@ -2,9 +2,7 @@ package net.tomatentum.cutin;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import net.tomatentum.cutin.method.ReflectedMethod; public class TestReflectedMethod extends ReflectedMethod<String> {
public class TestReflectedMethod extends ReflectedMethod<String, String> {
protected TestReflectedMethod(Object containingObject) { protected TestReflectedMethod(Object containingObject) {
super(getMethod(containingObject), containingObject); super(getMethod(containingObject), containingObject);
@ -12,15 +10,8 @@ public class TestReflectedMethod extends ReflectedMethod<String, String> {
} }
@Override @Override
public Object getParameter(String context, int index) { public Object getParameter(Object context, int index) {
switch (index) {
case 0:
return context;
case 1:
return 2; return 2;
default:
return null;
}
} }
@Override @Override

View File

@ -1,7 +0,0 @@
# SLF4J's SimpleLogger configuration file
# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err.
# Default logging detail level for all instances of SimpleLogger.
# Must be one of ("trace", "debug", "info", "warn", or "error").
# If not specified, defaults to "info".
org.slf4j.simpleLogger.defaultLogLevel=trace