LibJF/libjf-config-v0/src/main/java/io/gitlab/jfronny/libjf/config/impl/ConfigInstanceAbstract.java

173 lines
6.3 KiB
Java

package io.gitlab.jfronny.libjf.config.impl;
import io.gitlab.jfronny.commons.throwable.Coerce;
import io.gitlab.jfronny.gson.JsonElement;
import io.gitlab.jfronny.gson.JsonObject;
import io.gitlab.jfronny.gson.stream.JsonWriter;
import io.gitlab.jfronny.libjf.config.api.*;
import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe;
import io.gitlab.jfronny.libjf.unsafe.SafeLog;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
public abstract class ConfigInstanceAbstract implements ConfigInstance {
public static final String CONFIG_PRESET_DEFAULT = "libjf-config-v0.default";
public final String modId;
private final String categoryPath;
public final Class<?> configClass;
public final List<String> referencedConfigs;
public final List<EntryInfo<?>> entries = new ArrayList<>();
public final Map<String, Runnable> presets = new LinkedHashMap<>();
public final Set<Runnable> verifiers = new LinkedHashSet<>();
public final Map<String, ConfigInstance> subcategories = new LinkedHashMap<>();
public ConfigInstanceAbstract(String modId, String categoryPath, Class<?> configClass, AuxiliaryMetadata meta) {
this.modId = modId;
this.categoryPath = categoryPath;
this.configClass = configClass;
this.referencedConfigs = List.copyOf(meta.referencedConfigs);
for (Field field : configClass.getFields()) {
EntryInfo<?> newInfo = EntryInfo.fromField(field);
if (newInfo != null) entries.add(newInfo);
}
presets.put(CONFIG_PRESET_DEFAULT, () -> {
for (EntryInfo<?> entry : entries) {
try {
reset(entry);
} catch (IllegalAccessException e) {
SafeLog.error("Could not reload default values", e);
}
}
});
for (Method method : configClass.getMethods()) {
if (method.isAnnotationPresent(Preset.class)) {
presets.put(modId + ".jfconfig." + method.getName(), () -> {
try {
method.invoke(null);
} catch (IllegalAccessException | InvocationTargetException e) {
SafeLog.error("Could not apply preset", e);
}
});
}
else if (method.isAnnotationPresent(Verifier.class)) {
verifiers.add(() -> {
try {
method.invoke(null);
} catch (IllegalAccessException | InvocationTargetException e) {
SafeLog.error("Could not run verifier", e);
}
});
}
}
verifiers.add(() -> {
for (EntryInfo<?> entry : entries) {
entry.fix();
}
});
for (Class<?> categoryClass : configClass.getClasses()) {
if (categoryClass.isAnnotationPresent(Category.class)) {
String name = camelCase(categoryClass.getSimpleName());
//TODO allow custom auxiliary metadata
subcategories.put(name, new ConfigInstanceCategory(this, modId, categoryPath + name + ".", categoryClass, new AuxiliaryMetadata().sanitize()));
}
}
}
// Here due to generics issues
private <T> void reset(EntryInfo<T> info) throws IllegalAccessException {
info.setValue(info.getDefault());
}
private String camelCase(String source) {
if (source == null) return null;
if (source.length() == 0) return source;
return Character.toLowerCase(source.charAt(0)) + source.substring(1);
}
@Override
public void loadFrom(JsonObject source) {
for (EntryInfo<?> entry : entries) {
if (source.has(entry.getName())) {
try {
entry.loadFromJson(source.get(entry.getName()));
} catch (IllegalAccessException e) {
SafeLog.error("Could not set config entry value of " + entry.getName(), e);
}
} else SafeLog.error("Config does not contain entry for " + entry.getName());
}
for (Map.Entry<String, ConfigInstance> entry : subcategories.entrySet()) {
if (source.has(entry.getKey())) {
JsonElement el = source.get(entry.getKey());
if (el.isJsonObject()) entry.getValue().loadFrom(el.getAsJsonObject());
else SafeLog.error("Config category is not a JSON object, skipping");
} else SafeLog.error("Config does not contain entry for subcategory " + entry.getKey());
}
}
@Override
public void writeTo(JsonWriter writer) throws IOException {
verify();
writer.beginObject();
String val;
for (EntryInfo<?> entry : entries) {
try {
entry.writeTo(writer, modId + ".jfconfig." + categoryPath);
} catch (IllegalAccessException e) {
SafeLog.error("Could not write entry", e);
}
}
for (Map.Entry<String, ConfigInstance> entry : subcategories.entrySet()) {
if ((val = JfConfigSafe.TRANSLATION_SUPPLIER.apply(modId + ".jfconfig." + categoryPath + entry.getKey() + ".title")) != null)
writer.comment(val);
writer.name(entry.getKey());
entry.getValue().writeTo(writer);
}
writer.endObject();
}
@Override
public void verify() {
for (Runnable verifier : verifiers) verifier.run();
}
@Override
public boolean matchesConfigClass(Class<?> candidate) {
return candidate != null && candidate.isAssignableFrom(configClass);
}
@Override
public String getModId() {
return modId;
}
@Override
public String getCategoryPath() {
return categoryPath;
}
@Override
public List<EntryInfo<?>> getEntries() {
return entries;
}
@Override
public Map<String, Runnable> getPresets() {
return presets;
}
@Override
public List<String> getReferencedConfigs() {
return referencedConfigs;
}
@Override
public Map<String, ConfigInstance> getCategories() {
return subcategories;
}
}