LibJF/libjf-config-reflect-v1/src/main/java/io/gitlab/jfronny/libjf/config/impl/reflect/ReflectiveConfigBuilderImpl...

85 lines
4.0 KiB
Java

package io.gitlab.jfronny.libjf.config.impl.reflect;
import io.gitlab.jfronny.commons.reflect.Reflect;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.api.v1.*;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.CategoryBuilder;
import io.gitlab.jfronny.libjf.config.api.v1.dsl.ConfigBuilder;
import io.gitlab.jfronny.libjf.config.api.v1.reflect.ReflectiveConfigBuilder;
import io.gitlab.jfronny.libjf.config.impl.AuxiliaryMetadata;
import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.function.Function;
public class ReflectiveConfigBuilderImpl implements ReflectiveConfigBuilder {
private final AuxiliaryMetadata rootMeta;
private final Class<?> rootClass;
private final Class<?> rootTweaker;
private final String id;
public ReflectiveConfigBuilderImpl(String id, Class<?> klazz) {
this.id = id;
this.rootClass = Objects.requireNonNull(klazz);
JfConfig annotation = klazz.getAnnotation(JfConfig.class);
rootTweaker = annotation.tweaker();
this.rootMeta = AuxiliaryMetadata.of(annotation).merge(AuxiliaryMetadata.forMod(id));
}
@Override
public ConfigBuilder<?> apply(ConfigBuilder<?> builder) {
return applyCategory(builder, rootClass, findTweaker(rootTweaker, ConfigBuilder.class), rootMeta);
}
private <T extends CategoryBuilder<?>> T applyCategory(T builder, Class<?> configClass, Function<T, T> tweaker, AuxiliaryMetadata meta) {
meta.applyTo(builder);
for (Field field : configClass.getFields()) {
if (field.isAnnotationPresent(Entry.class)) {
builder.value(DslEntryInfo.ofField(field));
}
}
for (Method method : configClass.getMethods()) {
try {
if (method.isAnnotationPresent(Preset.class)) {
builder.addPreset(builder.getTranslationPrefix() + method.getName(), ReflectiveConfigBuilderImpl.<ConfigCategory>staticToConsumer(configClass, method).addHandler(e -> LibJf.LOGGER.error("Could not apply preset", e)));
} else if (method.isAnnotationPresent(Verifier.class)) {
builder.addVerifier(ReflectiveConfigBuilderImpl.<ConfigCategory>staticToConsumer(configClass, method).addHandler(e -> LibJf.LOGGER.error("Could not run verifier", e)));
}
} catch (Throwable t) {
LibJf.LOGGER.error("Could not process method " + method.getName() + " of config class " + configClass.getName());
}
}
for (Class<?> categoryClass : configClass.getClasses()) {
if (categoryClass.isAnnotationPresent(Category.class)) {
Category annotation = categoryClass.getAnnotation(Category.class);
String name = categoryClass.getSimpleName();
name = Character.toLowerCase(name.charAt(0)) + name.substring(1); // camelCase
var categoryTweaker = findTweaker(annotation.tweaker(), CategoryBuilder.class);
builder.category(name, builder1 -> applyCategory(builder1, categoryClass, categoryTweaker, AuxiliaryMetadata.of(categoryClass.getAnnotation(Category.class))));
}
}
return tweaker.apply(builder);
}
private <T> Function<T, T> findTweaker(Class<?> targetClass, Class<T> tweakedClass) {
try {
return Objects.equals(targetClass, void.class)
? Function.identity()
: Reflect.staticFunction(targetClass, "tweak", tweakedClass, tweakedClass);
} catch (Throwable t) {
LibJf.LOGGER.error("Could not find tweaker " + targetClass + " for mod " + id, t);
return Function.identity();
}
}
public static <T> ThrowingConsumer<T, Throwable> staticToConsumer(Class<?> klazz, Method method) throws Throwable {
Runnable rn = Reflect.staticProcedure(klazz, method.getName());
return c -> rn.run();
}
}