LibJF/libjf-config-core-v2/src/main/java/io/gitlab/jfronny/libjf/config/impl/dsl/DslEntryInfo.java

198 lines
7.3 KiB
Java

package io.gitlab.jfronny.libjf.config.impl.dsl;
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import io.gitlab.jfronny.gson.stream.*;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.config.api.v2.Entry;
import io.gitlab.jfronny.libjf.config.api.v2.EntryInfo;
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;
public class DslEntryInfo<T> implements EntryInfo<T> {
private final String name;
private final T defaultValue;
private final ThrowingSupplier<T, IllegalAccessException> get;
private final ThrowingConsumer<T, IllegalAccessException> set;
private final Type type;
private final int width;
private final double minValue;
private final double maxValue;
public DslEntryInfo(String name,
T defaultValue,
ThrowingSupplier<T, IllegalAccessException> get,
ThrowingConsumer<T, IllegalAccessException> set,
Type type,
int width,
double minValue,
double maxValue) {
this.name = name;
this.defaultValue = defaultValue;
this.get = get;
this.set = set;
this.type = type;
this.width = width;
this.minValue = minValue;
this.maxValue = maxValue;
}
public DslEntryInfo(String name,
T def,
ThrowingSupplier<T, IllegalAccessException> get,
ThrowingConsumer<T, IllegalAccessException> set,
Type type) {
this(name, def, get, set, type, 100, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
}
public static DslEntryInfo<Object> ofField(Field field) {
Entry entry = field.getAnnotation(Entry.class);
Object defaultValue = null;
try {
defaultValue = field.get(null);
} catch (IllegalAccessException ignored) {
}
//noinspection unchecked,rawtypes
return new DslEntryInfo<Object>(
field.getName(),
defaultValue,
() -> field.get(null),
v -> field.set(null, v),
(Type) Type.ofClass(field.getGenericType()),
entry == null ? 100 : entry.width(),
entry == null ? Double.NEGATIVE_INFINITY : entry.min(),
entry == null ? Double.POSITIVE_INFINITY : entry.max()
);
}
@Override
public String getName() {
return name;
}
@Override
public T getDefault() {
return defaultValue;
}
@Override
public T getValue() throws IllegalAccessException {
return get.get();
}
@Override
public void setValue(T value) throws IllegalAccessException {
set.accept(value);
}
@Override
public Type getValueType() {
return type;
}
@Override
public void fix() {
Object value;
try {
value = getValue();
} catch (IllegalAccessException e) {
LibJf.LOGGER.error("Could not read value", e);
return;
}
final Object valueOriginal = value;
if (value instanceof final Integer v) {
if (v < minValue) value = (int)minValue;
if (v > maxValue) value = (int)maxValue;
} else if (value instanceof final Float v) {
if (v < minValue) value = (float)minValue;
if (v > maxValue) value = (float)maxValue;
} else if (value instanceof final Double v) {
if (v < minValue) value = minValue;
if (v > maxValue) value = maxValue;
}
if (valueOriginal != value) {
try {
setUnchecked(value);
} catch (IllegalAccessException e) {
LibJf.LOGGER.error("Could not write value", e);
}
}
}
@Override
public void loadFromJson(JsonReader reader) throws IOException, IllegalAccessException {
var next = reader.peek();
if (type.isBool()) {
if (next == JsonToken.BOOLEAN) setUnchecked(reader.nextBoolean());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected boolean but got " + next);
} else if (type.isString()) {
if (next == JsonToken.STRING || next == JsonToken.NUMBER) setUnchecked(reader.nextString());
else if (next == JsonToken.BOOLEAN) setUnchecked(Boolean.toString(reader.nextBoolean()));
else if (next == JsonToken.NULL) setUnchecked(null);
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected string but got " + next);
} else if (type.isInt()) {
if (next == JsonToken.NUMBER) setUnchecked(reader.nextInt());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isLong()) {
if (next == JsonToken.NUMBER) setUnchecked(reader.nextLong());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isDouble()) {
if (next == JsonToken.NUMBER) {
setUnchecked(reader.nextDouble());
}
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isFloat()) {
if (next == JsonToken.NUMBER) setUnchecked((float) reader.nextDouble());
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
} else if (type.isEnum()) {
Type.TEnum<T> e = (Type.TEnum<T>) type;
if (next == JsonToken.STRING) setUnchecked(e.optionForString(reader.nextString()));
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected string but got " + next);
} else {
setValue(GsonHolders.CONFIG.getGson().fromJson(reader, type.asClass()));
}
}
@SuppressWarnings("unchecked")
private void setUnchecked(Object object) throws IllegalAccessException {
if (object == null) return;
setValue((T) object);
}
@Override
public void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException {
T value = getValue();
String commentText;
if ((commentText = JfConfigSafe.TRANSLATION_SUPPLIER.apply(translationPrefix + getName() + ".tooltip")) != null) {
writer.comment(commentText);
}
if (type.isEnum()) {
writer.comment("Valid: [" + Arrays.stream(((Type.TEnum<T>)type).options()).map(Objects::toString).collect(Collectors.joining(", ")) + "]");
}
writer.name(name);
GsonHolders.CONFIG.getGson().toJson(value, Objects.requireNonNullElse(type.asClass(), String.class), writer);
}
@Override
public int getWidth() {
return width;
}
@Override
public double getMinValue() {
return minValue;
}
@Override
public double getMaxValue() {
return maxValue;
}
}