Compare commits

8 Commits

Author SHA1 Message Date
d76db0ba61 bump Minor version
All checks were successful
Publish / Gradle-Publish (push) Successful in 12s
Build / Gradle-Build (push) Successful in 9s
Test / Gradle-Test (push) Successful in 12s
2025-04-15 23:25:45 +02:00
8d24604707 feat(factory): add utility method which does the "unchecked" cast
All checks were successful
Build / Gradle-Build (push) Successful in 1m36s
Publish / Gradle-Publish (push) Successful in 10s
Test / Gradle-Test (push) Successful in 12s
2025-04-15 11:06:00 +02:00
28d991f17c fix(log): missing placeholder 2025-04-15 11:03:33 +02:00
4c800cfd89 fix: compile error
All checks were successful
Build / Gradle-Build (push) Successful in 9s
Publish / Gradle-Publish (push) Successful in 10s
Test / Gradle-Test (push) Successful in 11s
2025-04-14 19:04:09 +02:00
e425989106 feat(parser): MethodParser rework
Some checks failed
Build / Gradle-Build (push) Failing after 9s
Publish / Gradle-Publish (push) Failing after 8s
Test / Gradle-Test (push) Failing after 9s
2025-04-14 19:01:01 +02:00
eccad71837 feat(logging): add more logging
All checks were successful
Build / Gradle-Build (push) Successful in 13s
Publish / Gradle-Publish (push) Successful in 10s
Test / Gradle-Test (push) Successful in 11s
2025-04-14 18:34:46 +02:00
cad019e44a fix(container): NullPointerException when trying to query a key which is not in the container yet
All checks were successful
Build / Gradle-Build (push) Successful in 9s
Publish / Gradle-Publish (push) Successful in 9s
Test / Gradle-Test (push) Successful in 11s
2025-04-13 16:28:16 +02:00
79dcc25afc fix(method): add toString override
All checks were successful
Build / Gradle-Build (push) Successful in 1m38s
Publish / Gradle-Publish (push) Successful in 10s
Test / Gradle-Test (push) Successful in 11s
2025-04-13 15:09:03 +02:00
12 changed files with 79 additions and 35 deletions

View File

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

View File

@@ -8,3 +8,4 @@ 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"}
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j"}

View File

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

View File

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

View File

@@ -1,8 +1,9 @@
package net.tomatentum.cutin;
import java.lang.reflect.Method;
import java.util.List;
import java.util.HashMap;
import java.util.Optional;
import java.util.Set;
import net.tomatentum.cutin.method.ReflectedMethod;
@@ -13,8 +14,27 @@ public interface ReflectedMethodFactory<I extends Object, C extends Object> {
public interface Factory<I extends Object, C extends Object> {
Optional<ReflectedMethod<I, C>> produce(Method method, Object containingObject);
void addParser(ReflectedMethod<I, C> method, List<MethodParser> parser);
Optional<ReflectedMethod<I, C>> produce(Method method, Object containingObject, ParserResults parserResults);
void addParser(Set<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,8 +2,10 @@ package net.tomatentum.cutin;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,9 +36,10 @@ public class ReflectedMethodFactoryImpl<I extends Object, C extends Object> impl
.map(Optional::get)
.findFirst();
if (rmethod.isEmpty()) {
logger.debug("Could not produce a ReflectedMethod for Method {}", ReflectionUtil.getFullMethodName(method));
}
if (rmethod.isEmpty())
logger.warn("Could not produce a ReflectedMethod for Method {} in {}", ReflectionUtil.getFullMethodName(method), this);
else
logger.debug("Produced {} for Method {} in {}", rmethod.get(), ReflectionUtil.getFullMethodName(method), this);
return rmethod;
}
@@ -44,17 +47,15 @@ public class ReflectedMethodFactoryImpl<I extends Object, C extends Object> impl
@Override
public ReflectedMethodFactory<I, C> addFactory(Factory<I, C> factory) {
this.factories.add(factory);
logger.trace("Added Factory {} to {}", factory, this);
return this;
}
private Optional<ReflectedMethod<I, C>> factoryProduce(Factory<I, C> factory, Method method, Object containingClass) {
List<MethodParser> parser = new ArrayList<>();
Optional<ReflectedMethod<I, C>> m = factory.produce(method, containingClass);
m.ifPresent(x -> {
factory.addParser(x, parser);
parser.forEach(MethodParser::parse);
});
return m;
Set<MethodParser> parser = new HashSet<>();
factory.addParser(parser);
ParserResults results = ParserResults.create(parser, method, containingClass);
return factory.produce(method, containingClass, results);
}
}

View File

@@ -3,16 +3,22 @@ package net.tomatentum.cutin.container;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.tomatentum.cutin.ReflectedMethodFactory;
import net.tomatentum.cutin.method.ReflectedMethod;
public class LoneMethodContainer<I extends Object, C extends Object> implements MethodContainer<I, C> {
private Logger logger = LoggerFactory.getLogger(getClass());
private Map<I, ReflectedMethod<I, C>> methodStore;
private ReflectedMethodFactory<I, C> factory;
@@ -24,6 +30,7 @@ public class LoneMethodContainer<I extends Object, C extends Object> implements
@Override
public MethodContainer<I, C> addMethod(ReflectedMethod<I, C> method) {
this.methodStore.put(method.identifier(), method);
logger.debug("Added {} to container", method);
return this;
}
@@ -47,7 +54,8 @@ public class LoneMethodContainer<I extends Object, C extends Object> implements
@Override
public Collection<ReflectedMethod<I, C>> findFor(I identifier) {
return Arrays.asList(this.methodStore.get(identifier));
ReflectedMethod<I, C> result = this.methodStore.get(identifier);
return result != null ? Arrays.asList(result) : Collections.emptyList();
}
@Override

View File

@@ -95,21 +95,10 @@ public class MultiMethodContainer<I extends Object, C extends Object> implements
throw new IllegalArgumentException("Method's identifier did not equal the entry's identifier");
this.methods.add(method);
logger.debug("Added method {} to entry {}", method.method().getName(), this);
logger.debug("Added method {} to entry {}", ReflectionUtil.getFullMethodName(method.method()), this);
return this;
}
public Object[] runAll(C 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
public boolean equals(Object obj) {
if (!(obj instanceof Entry))
@@ -125,7 +114,7 @@ public class MultiMethodContainer<I extends Object, C extends Object> implements
@Override
public String toString() {
return "Content(%s)".formatted(identifier().toString());
return "Ident(%s)".formatted(identifier().toString());
}
}

View File

@@ -7,12 +7,18 @@ 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) {
@@ -30,6 +36,7 @@ public abstract class BestCandidateMethod<I extends Object, C extends Object> ex
.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);
}
@@ -75,14 +82,14 @@ public abstract class BestCandidateMethod<I extends Object, C extends Object> ex
}
@Override
public Optional<ReflectedMethod<I, C>> produce(Method method, Object containingObject) {
Optional<BestCandidateMethod<I, C>> bcMethod = bcProduce(methodName, containingObject);
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);
protected abstract Optional<BestCandidateMethod<I, C>> bcProduce(String methodName, Object containingObject, ParserResults results);
}

View File

@@ -33,9 +33,10 @@ public abstract class ReflectedMethod<I extends Object, C extends Object> {
public Object run(C context) {
method.setAccessible(true);
try {
logger.debug("Invoking method {} from {}", ReflectionUtil.getFullMethodName(method), this);
return method.invoke(containingObject, getParameters(context));
}catch (IllegalAccessException | InvocationTargetException ex) {
logger.error("ReflectedMethod failed to run", ex);
logger.error("ReflectedMethod %s failed to run".formatted(this), ex);
return null;
}
}
@@ -48,13 +49,18 @@ public abstract class ReflectedMethod<I extends Object, C extends Object> {
return this.containingObject;
}
@Override
public String toString() {
return "ReflectedMethod(%s)".formatted(identifier());
}
private Object[] getParameters(C context) {
int parameterCount = method.getParameterCount();
List<Object> parameters = new ArrayList<>();
for (int i = 0; i < parameterCount; i++) {
Object parameter = getParameter(context, i);
logger.trace("Found parameter {}={} for method {}", parameter != null ? parameter.getClass().toString() : " ", parameter, ReflectionUtil.getFullMethodName(method));
logger.trace("Found parameter {}={} for method {} in {}", parameter != null ? parameter.getClass().toString() : " ", parameter, ReflectionUtil.getFullMethodName(method), this);
parameters.add(parameter);
}
return parameters.toArray();

View File

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

View File

@@ -0,0 +1,7 @@
# 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