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 applyCategory(T builder, Class configClass, Function 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.staticToConsumer(configClass, method).addHandler(e -> LibJf.LOGGER.error("Could not apply preset", e))); } else if (method.isAnnotationPresent(Verifier.class)) { builder.addVerifier(ReflectiveConfigBuilderImpl.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 Function findTweaker(Class targetClass, Class 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 ThrowingConsumer staticToConsumer(Class klazz, Method method) throws Throwable { Runnable rn = Reflect.staticProcedure(klazz, method.getName()); return c -> rn.run(); } }