mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 10:38:19 +08:00
Merge branch '4.0.0'
# Conflicts: # providers/yaml/pom.xml
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
package cc.carm.lib.configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The root interface of the configuration file interfaces,
|
||||||
|
* which is used to label a class as a configuration.
|
||||||
|
*/
|
||||||
|
public interface Configuration { }
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value adapter, used to convert the value of the configuration file into the objects.
|
||||||
|
*
|
||||||
|
* @param <TYPE> The type of the target value
|
||||||
|
*/
|
||||||
|
public class ValueAdapter<TYPE>
|
||||||
|
implements ValueSerializer<TYPE>, ValueParser<TYPE> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<TYPE> type;
|
||||||
|
protected @Nullable ValueSerializer<TYPE> serializer;
|
||||||
|
protected @Nullable ValueParser<TYPE> deserializer;
|
||||||
|
|
||||||
|
public ValueAdapter(@NotNull ValueType<TYPE> type) {
|
||||||
|
this(type, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueAdapter(@NotNull ValueType<TYPE> type,
|
||||||
|
@Nullable ValueSerializer<TYPE> serializer,
|
||||||
|
@Nullable ValueParser<TYPE> parser) {
|
||||||
|
this.type = type;
|
||||||
|
this.serializer = serializer;
|
||||||
|
this.deserializer = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueType<TYPE> type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable ValueSerializer<TYPE> serializer() {
|
||||||
|
return serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable ValueParser<TYPE> parser() {
|
||||||
|
return deserializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueAdapter<TYPE> serializer(@Nullable ValueSerializer<TYPE> serializer) {
|
||||||
|
this.serializer = serializer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueAdapter<TYPE> parser(@Nullable ValueParser<TYPE> deserializer) {
|
||||||
|
this.deserializer = deserializer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object serialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull TYPE value) throws Exception {
|
||||||
|
if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported");
|
||||||
|
return serializer.serialize(holder, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TYPE parse(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull Object value) throws Exception {
|
||||||
|
if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported");
|
||||||
|
return deserializer.parse(holder, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof ValueAdapter)) return false;
|
||||||
|
ValueAdapter<?> that = (ValueAdapter<?>) o;
|
||||||
|
return Objects.equals(type, that.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ValueAdapterRegistry {
|
||||||
|
|
||||||
|
protected final Set<ValueAdapter<?>> adapters = new HashSet<>();
|
||||||
|
|
||||||
|
public <FROM, TO> void register(@NotNull Class<FROM> from, @NotNull Class<TO> to,
|
||||||
|
@Nullable DataFunction<FROM, TO> parser,
|
||||||
|
@Nullable DataFunction<TO, FROM> serializer) {
|
||||||
|
register(ValueType.of(from), ValueType.of(to), parser, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <FROM, TO> void register(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
|
||||||
|
@Nullable DataFunction<FROM, TO> parser,
|
||||||
|
@Nullable DataFunction<TO, FROM> serializer) {
|
||||||
|
ValueAdapter<FROM> fromAdapter = adapterOf(from);
|
||||||
|
if (fromAdapter == null) throw new IllegalArgumentException("No adapter for type " + from);
|
||||||
|
register(to,
|
||||||
|
serializer == null ? null : (provider, type, value) -> fromAdapter.serialize(provider, from, serializer.handle(value)),
|
||||||
|
parser == null ? null : (provider, type, data) -> parser.handle(fromAdapter.parse(provider, from, data))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register(@NotNull ValueAdapter<?>... adapter) {
|
||||||
|
adapters.addAll(Arrays.asList(adapter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void register(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||||
|
register(ValueType.of(type), serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void register(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||||
|
ValueAdapter<T> existing = adapterOf(type);
|
||||||
|
if (existing != null) {
|
||||||
|
existing.serializer(serializer);
|
||||||
|
} else {
|
||||||
|
register(new ValueAdapter<>(type, serializer, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void register(@NotNull Class<T> type, @NotNull ValueParser<T> deserializer) {
|
||||||
|
register(ValueType.of(type), deserializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void register(@NotNull ValueType<T> type, @NotNull ValueParser<T> deserializer) {
|
||||||
|
ValueAdapter<T> existing = adapterOf(type);
|
||||||
|
if (existing != null) {
|
||||||
|
existing.parser(deserializer);
|
||||||
|
} else {
|
||||||
|
register(new ValueAdapter<>(type, null, deserializer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void register(@NotNull ValueType<T> type, @Nullable ValueSerializer<T> serializer, @Nullable ValueParser<T> deserializer) {
|
||||||
|
if (serializer == null && deserializer == null) return;
|
||||||
|
ValueAdapter<T> existing = adapterOf(type);
|
||||||
|
if (existing != null) {
|
||||||
|
if (serializer != null) existing.serializer(serializer);
|
||||||
|
if (deserializer != null) existing.parser(deserializer);
|
||||||
|
} else {
|
||||||
|
register(new ValueAdapter<>(type, serializer, deserializer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister(@NotNull Class<?> type) {
|
||||||
|
unregister(ValueType.of(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister(@NotNull ValueType<?> type) {
|
||||||
|
adapters.removeIf(adapter -> adapter.type().equals(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> @Nullable ValueAdapter<T> adapterOf(@NotNull ValueType<T> type) {
|
||||||
|
ValueAdapter<?> matched = adapters.stream().filter(adapter -> adapter.type().equals(type)).findFirst().orElse(null);
|
||||||
|
if (matched != null) return (ValueAdapter<T>) matched;
|
||||||
|
|
||||||
|
// If no adapter found, try to find the adapter for the super type
|
||||||
|
return (ValueAdapter<T>) adapters.stream()
|
||||||
|
.filter(adapter -> adapter.type().isSubtypeOf(type))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> ValueAdapter<T> adapterOf(@NotNull T value) {
|
||||||
|
return adapterOf(ValueType.of(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> ValueAdapter<T> adapterOf(@NotNull Class<T> type) {
|
||||||
|
return adapterOf(ValueType.of(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_,_,null -> null")
|
||||||
|
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||||
|
return deserialize(holder, ValueType.of(type), source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_,_,null -> null")
|
||||||
|
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||||
|
if (source == null) return null; // Null check
|
||||||
|
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize
|
||||||
|
ValueAdapter<T> adapter = adapterOf(type);
|
||||||
|
if (adapter == null) throw new RuntimeException("No adapter for type " + type);
|
||||||
|
return adapter.parse(holder, type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_,null -> null")
|
||||||
|
public <T> Object serialize(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception {
|
||||||
|
if (value == null) return null; // Null check
|
||||||
|
ValueType<T> type = ValueType.of(value);
|
||||||
|
ValueAdapter<T> adapter = adapterOf(type);
|
||||||
|
if (adapter == null) return value; // No adapters, try to return the original value
|
||||||
|
return adapter.serialize(holder, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value deserializer, convert base data to target value.
|
||||||
|
*
|
||||||
|
* @param <TYPE> The type of target value
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueParser<TYPE> {
|
||||||
|
|
||||||
|
TYPE parse(
|
||||||
|
@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull ValueType<? super TYPE> type, @NotNull Object data
|
||||||
|
) throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value serializer, convert target value to base data.
|
||||||
|
*
|
||||||
|
* @param <TYPE> The type of value
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueSerializer<TYPE> {
|
||||||
|
|
||||||
|
Object serialize(
|
||||||
|
@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull ValueType<? super TYPE> type, @NotNull TYPE value
|
||||||
|
) throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to get the generic type.
|
||||||
|
*/
|
||||||
|
public abstract class ValueType<T> {
|
||||||
|
|
||||||
|
public static final ValueType<String> STRING = ofPrimitiveType(String.class);
|
||||||
|
public static final ValueType<Integer> INTEGER = ofPrimitiveType(Integer.class);
|
||||||
|
public static final ValueType<Integer> INTEGER_TYPE = ofPrimitiveType(int.class);
|
||||||
|
public static final ValueType<Long> LONG = ofPrimitiveType(Long.class);
|
||||||
|
public static final ValueType<Long> LONG_TYPE = ofPrimitiveType(long.class);
|
||||||
|
public static final ValueType<Double> DOUBLE = ofPrimitiveType(Double.class);
|
||||||
|
public static final ValueType<Double> DOUBLE_TYPE = ofPrimitiveType(double.class);
|
||||||
|
public static final ValueType<Float> FLOAT = ofPrimitiveType(Float.class);
|
||||||
|
public static final ValueType<Float> FLOAT_TYPE = ofPrimitiveType(float.class);
|
||||||
|
public static final ValueType<Boolean> BOOLEAN = ofPrimitiveType(Boolean.class);
|
||||||
|
public static final ValueType<Boolean> BOOLEAN_TYPE = ofPrimitiveType(boolean.class);
|
||||||
|
public static final ValueType<Byte> BYTE = ofPrimitiveType(Byte.class);
|
||||||
|
public static final ValueType<Byte> BYTE_TYPE = ofPrimitiveType(byte.class);
|
||||||
|
public static final ValueType<Short> SHORT = ofPrimitiveType(Short.class);
|
||||||
|
public static final ValueType<Short> SHORT_TYPE = ofPrimitiveType(short.class);
|
||||||
|
public static final ValueType<Character> CHAR = ofPrimitiveType(Character.class);
|
||||||
|
public static final ValueType<Character> CHAR_TYPE = ofPrimitiveType(char.class);
|
||||||
|
|
||||||
|
public static final ValueType<?>[] PRIMITIVE_TYPES = {
|
||||||
|
STRING, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, BYTE, SHORT, CHAR,
|
||||||
|
INTEGER_TYPE, LONG_TYPE, DOUBLE_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE
|
||||||
|
};
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> ValueType<T> of(@NotNull T value) {
|
||||||
|
return of((Class<T>) value.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> ValueType<T> of(final Type type) {
|
||||||
|
if (type == null) throw new NullPointerException("Type cannot be null");
|
||||||
|
if (type instanceof Class<?>) { // Try handle primitive types
|
||||||
|
Class<?> clazz = (Class<?>) type;
|
||||||
|
for (ValueType<?> valueType : PRIMITIVE_TYPES) {
|
||||||
|
if (valueType.getRawType() == clazz) {
|
||||||
|
return (ValueType<T>) valueType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ValueType<T>(type) {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ValueType<T> of(final Class<T> clazz) {
|
||||||
|
return of((Type) clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the generic type of the complex type.
|
||||||
|
*
|
||||||
|
* @param rawType The raw type
|
||||||
|
* @param types The type arguments
|
||||||
|
* @param <T> The type
|
||||||
|
* @return The {@link ValueType}
|
||||||
|
*/
|
||||||
|
public static <T> ValueType<T> of(final Class<?> rawType, final Type... types) {
|
||||||
|
ParameterizedType parameterizedType = new ParameterizedType() {
|
||||||
|
@Override
|
||||||
|
public @NotNull Type @NotNull [] getActualTypeArguments() {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Type getRawType() {
|
||||||
|
return rawType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getOwnerType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return of(parameterizedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> ValueType<T> ofPrimitiveType(Class<T> clazz) {
|
||||||
|
return new ValueType<T>(clazz) {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
protected ValueType() {
|
||||||
|
this.type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueType(Type type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSubtypeOf(Class<?> target) {
|
||||||
|
Class<?> rawType = getRawType();
|
||||||
|
return target.isAssignableFrom(rawType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSubtypeOf(ValueType<?> target) {
|
||||||
|
return target.isSubtypeOf(getRawType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInstance(Object obj) {
|
||||||
|
return obj != null && getRawType().isInstance(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取当前 ValueType 的原始类型(Class 对象)。
|
||||||
|
*
|
||||||
|
* @return 对应的 Class 对象
|
||||||
|
* @throws IllegalStateException 如果无法提取出原始类型
|
||||||
|
*/
|
||||||
|
public Class<?> getRawType() {
|
||||||
|
if (type instanceof Class<?>) {
|
||||||
|
return (Class<?>) type;
|
||||||
|
}
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
ParameterizedType pt = (ParameterizedType) type;
|
||||||
|
Type raw = pt.getRawType();
|
||||||
|
if (raw instanceof Class<?>) {
|
||||||
|
return (Class<?>) raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Unsupported type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T cast(Object obj) {
|
||||||
|
if (!isInstance(obj)) {
|
||||||
|
throw new ClassCastException("Cannot cast object " + obj + " to type " + this);
|
||||||
|
}
|
||||||
|
return (T) obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (type instanceof Class<?>) {
|
||||||
|
return ((Class<?>) type).getName();
|
||||||
|
}
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
ParameterizedType pt = (ParameterizedType) type;
|
||||||
|
Type raw = pt.getRawType();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(raw.getTypeName());
|
||||||
|
sb.append('<');
|
||||||
|
Type[] args = pt.getActualTypeArguments();
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(args[i].getTypeName());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
return type.getTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) return true;
|
||||||
|
if (obj instanceof ValueType) {
|
||||||
|
return Objects.equals(type, ((ValueType<?>) obj).type);
|
||||||
|
}
|
||||||
|
if (obj instanceof Type) {
|
||||||
|
return Objects.equals(type, obj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter.strandard;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class PrimitiveAdapter<T> extends ValueAdapter<T> {
|
||||||
|
|
||||||
|
public static final PrimitiveAdapter<?>[] ADAPTERS = new PrimitiveAdapter[]{
|
||||||
|
ofString(), ofBoolean(), ofBooleanType(), ofCharacter(), ofCharacterType(),
|
||||||
|
ofInteger(), ofIntegerType(), ofLong(), ofLongType(), ofDouble(), ofDoubleType(),
|
||||||
|
ofFloat(), ofFloatType(), ofShort(), ofShortType(), ofByte(), ofByteType()
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final String[] TRUE_VALUES = new String[]{
|
||||||
|
"true", "yes", "on", "1", "enabled", "enable", "active"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final String[] FALSE_VALUES = new String[]{
|
||||||
|
"false", "no", "off", "0", "disabled", "disable", "inactive"
|
||||||
|
};
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
public static ValueAdapter<Enum<?>> ofEnum() {
|
||||||
|
ValueAdapter<Enum<?>> adapter = new ValueAdapter<>(new ValueType<Enum<?>>() {
|
||||||
|
});
|
||||||
|
adapter.parser((provider, type, data) -> Enum.valueOf((Class<Enum>) type.getRawType(), data.toString()));
|
||||||
|
adapter.serializer((provider, type, value) -> value.name());
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<String> ofString() {
|
||||||
|
return of(String.class, o -> o instanceof String ? (String) o : o.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Boolean> ofBoolean() {
|
||||||
|
return of(Boolean.class, data -> {
|
||||||
|
if (data instanceof Boolean) return (Boolean) data;
|
||||||
|
String v = data.toString().trim();
|
||||||
|
if (Arrays.stream(TRUE_VALUES).anyMatch(v::equalsIgnoreCase)) return true;
|
||||||
|
else if (Arrays.stream(FALSE_VALUES).anyMatch(v::equalsIgnoreCase)) return false;
|
||||||
|
else throw new IllegalArgumentException("Cannot parse boolean from " + data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Boolean> ofBooleanType() {
|
||||||
|
return of(boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Character> ofCharacter() {
|
||||||
|
return of(Character.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Character> ofCharacterType() {
|
||||||
|
return of(char.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Integer> ofInteger() {
|
||||||
|
return ofNumber(Integer.class, Number::intValue, Integer::parseInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Integer> ofIntegerType() {
|
||||||
|
return ofNumber(int.class, Number::intValue, Integer::parseInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Long> ofLong() {
|
||||||
|
return ofNumber(Long.class, Number::longValue, Long::parseLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Long> ofLongType() {
|
||||||
|
return ofNumber(long.class, Number::longValue, Long::parseLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Double> ofDouble() {
|
||||||
|
return ofNumber(Double.class, Number::doubleValue, Double::parseDouble);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Double> ofDoubleType() {
|
||||||
|
return ofNumber(double.class, Number::doubleValue, Double::parseDouble);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Float> ofFloat() {
|
||||||
|
return ofNumber(Float.class, Number::floatValue, Float::parseFloat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Float> ofFloatType() {
|
||||||
|
return ofNumber(float.class, Number::floatValue, Float::parseFloat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Short> ofShort() {
|
||||||
|
return ofNumber(Short.class, Number::shortValue, Short::parseShort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Short> ofShortType() {
|
||||||
|
return ofNumber(short.class, Number::shortValue, Short::parseShort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Byte> ofByte() {
|
||||||
|
return ofNumber(Byte.class, Number::byteValue, Byte::parseByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PrimitiveAdapter<Byte> ofByteType() {
|
||||||
|
return ofNumber(byte.class, Number::byteValue, Byte::parseByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> PrimitiveAdapter<T> of(@NotNull Class<T> clazz,
|
||||||
|
@NotNull DataFunction<Object, T> function) {
|
||||||
|
return new PrimitiveAdapter<>(clazz, (p, type, data) -> function.handle(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Number> PrimitiveAdapter<T> ofNumber(@NotNull Class<T> numberClass,
|
||||||
|
@NotNull DataFunction<Number, T> castFunction,
|
||||||
|
@NotNull DataFunction<String, T> parseFunction) {
|
||||||
|
return of(numberClass, o -> o instanceof Number ? castFunction.handle((Number) o) : parseFunction.handle(o.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PrimitiveAdapter(@NotNull Class<T> valueType, @NotNull ValueParser<T> deserializer) {
|
||||||
|
super(ValueType.of(valueType), (provider, type, value) -> value, deserializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter.strandard;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
|
||||||
|
public interface StandardAdapters {
|
||||||
|
|
||||||
|
ValueAdapter<ConfigureSection> SECTION_ADAPTER = new ValueAdapter<>(
|
||||||
|
ValueType.of(ConfigureSection.class),
|
||||||
|
(provider, type, value) -> value,
|
||||||
|
(provider, type, value) -> {
|
||||||
|
if (value instanceof ConfigureSection) {
|
||||||
|
return (ConfigureSection) value;
|
||||||
|
} else throw new IllegalArgumentException("Value is not a ConfigurationSection");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
+2
-4
@@ -1,6 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.core.annotation;
|
package cc.carm.lib.configuration.annotation;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@@ -16,7 +14,7 @@ public @interface ConfigPath {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The path value of the current configuration.
|
* The path value of the current configuration.
|
||||||
* If not set,will generate the path by {@link ConfigInitializer#getPathFromName(String)}.
|
* If not set,will generate the path by {@link cc.carm.lib.configuration.source.loader.PathGenerator}.
|
||||||
*
|
*
|
||||||
* @return The path value of the current configuration
|
* @return The path value of the current configuration
|
||||||
*/
|
*/
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package cc.carm.lib.configuration.builder;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public abstract class AbstractConfigBuilder<
|
||||||
|
TYPE, RESULT extends ConfigValue<TYPE>, PROVIDER extends ConfigurationHolder<?>,
|
||||||
|
SELF extends AbstractConfigBuilder<TYPE, RESULT, PROVIDER, SELF>
|
||||||
|
> {
|
||||||
|
|
||||||
|
protected final Class<? super PROVIDER> providerClass;
|
||||||
|
protected final ValueType<TYPE> type;
|
||||||
|
|
||||||
|
protected @Nullable PROVIDER provider;
|
||||||
|
protected @Nullable String path;
|
||||||
|
|
||||||
|
protected @NotNull Supplier<TYPE> defaultValueSupplier = () -> null;
|
||||||
|
protected @NotNull BiConsumer<ConfigurationHolder<?>, String> initializer = (provider, path) -> {
|
||||||
|
};
|
||||||
|
|
||||||
|
protected AbstractConfigBuilder(Class<? super PROVIDER> providerClass, ValueType<TYPE> type) {
|
||||||
|
this.providerClass = providerClass;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueType<TYPE> type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract @NotNull SELF self();
|
||||||
|
|
||||||
|
public abstract @NotNull RESULT build();
|
||||||
|
|
||||||
|
public @NotNull SELF holder(@Nullable PROVIDER provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF path(@Nullable String path) {
|
||||||
|
this.path = path;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF initializer(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||||
|
this.initializer = initializer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||||
|
return initializer(initializer.andThen(initializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) {
|
||||||
|
return append((provider, path) -> initializer.accept(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF defaults(@Nullable TYPE defaultValue) {
|
||||||
|
return defaults(() -> defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) {
|
||||||
|
this.defaultValueSupplier = supplier;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <M> @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
|
||||||
|
return append((provider, path) -> metaConsumer.accept(provider.metadata(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <M> @NotNull SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
|
||||||
|
return meta(holder -> holder.set(type, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected @NotNull ValueManifest<TYPE> buildManifest() {
|
||||||
|
return new ValueManifest<>(
|
||||||
|
type(), this.defaultValueSupplier, this.initializer,
|
||||||
|
this.provider, this.path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package cc.carm.lib.configuration.builder;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
|
||||||
|
public abstract class CommonConfigBuilder<TYPE, RESULT extends ConfigValue<TYPE>, SELF extends CommonConfigBuilder<TYPE, RESULT, SELF>>
|
||||||
|
extends AbstractConfigBuilder<TYPE, RESULT, ConfigurationHolder<?>, SELF> {
|
||||||
|
|
||||||
|
protected CommonConfigBuilder(ValueType<TYPE> type) {
|
||||||
|
super(ConfigurationHolder.class, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.impl;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public abstract class AbstractSectionBuilder<
|
||||||
|
TYPE, PARAM,
|
||||||
|
RESULT extends ConfigValue<TYPE>,
|
||||||
|
SELF extends AbstractSectionBuilder<TYPE, PARAM, RESULT, SELF>
|
||||||
|
> extends CommonConfigBuilder<TYPE, RESULT, SELF> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<PARAM> paramType;
|
||||||
|
|
||||||
|
protected @NotNull ValueHandler<ConfigureSection, PARAM> parser;
|
||||||
|
protected @NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer;
|
||||||
|
|
||||||
|
public AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<PARAM> paramType,
|
||||||
|
@NotNull ValueHandler<ConfigureSection, PARAM> parser,
|
||||||
|
@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
||||||
|
super(type);
|
||||||
|
this.paramType = paramType;
|
||||||
|
this.parser = parser;
|
||||||
|
this.serializer = serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(DataFunction<ConfigureSection, PARAM> valueParser) {
|
||||||
|
return parse((p, section) -> valueParser.handle(section));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(ValueHandler<ConfigureSection, PARAM> valueParser) {
|
||||||
|
this.parser = valueParser;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(DataFunction<PARAM, ? extends Map<String, Object>> serializer) {
|
||||||
|
return serialize((p, value) -> serializer.handle(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
||||||
|
this.serializer = serializer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(Consumer<Map<String, Object>> serializer) {
|
||||||
|
return serialize((p, value) -> {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
serializer.accept(map);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ValueAdapter<PARAM> buildAdapter() {
|
||||||
|
return new ValueAdapter<>(this.paramType)
|
||||||
|
.parser((p, type, data) -> {
|
||||||
|
ConfigureSection section = p.deserialize(ConfigureSection.class, data);
|
||||||
|
if (section == null) return null;
|
||||||
|
return this.parser.handle(p, section);
|
||||||
|
})
|
||||||
|
.serializer((p, type, data) -> {
|
||||||
|
Map<String, Object> map = this.serializer.handle(p, data);
|
||||||
|
return map == null || map.isEmpty() ? null : map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.impl;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public abstract class AbstractSourceBuilder<
|
||||||
|
V, SOURCE, PARAM, RESULT extends ConfigValue<V>,
|
||||||
|
SELF extends AbstractSourceBuilder<V, SOURCE, PARAM, RESULT, SELF>
|
||||||
|
> extends CommonConfigBuilder<V, RESULT, SELF> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<SOURCE> sourceType;
|
||||||
|
protected final @NotNull ValueType<PARAM> paramType;
|
||||||
|
protected @NotNull ValueHandler<SOURCE, PARAM> valueParser;
|
||||||
|
protected @NotNull ValueHandler<PARAM, SOURCE> valueSerializer;
|
||||||
|
|
||||||
|
public AbstractSourceBuilder(@NotNull ValueType<V> type,
|
||||||
|
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<PARAM> paramType,
|
||||||
|
@NotNull ValueHandler<SOURCE, PARAM> parser,
|
||||||
|
@NotNull ValueHandler<PARAM, SOURCE> serializer) {
|
||||||
|
super(type);
|
||||||
|
this.sourceType = sourceType;
|
||||||
|
this.paramType = paramType;
|
||||||
|
this.valueParser = parser;
|
||||||
|
this.valueSerializer = serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(DataFunction<SOURCE, PARAM> parser) {
|
||||||
|
return parse((p, source) -> parser.handle(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, PARAM> parser) {
|
||||||
|
this.valueParser = parser;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, SOURCE> serializer) {
|
||||||
|
this.valueSerializer = serializer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, SOURCE> serializer) {
|
||||||
|
return serialize((p, value) -> serializer.handle(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ValueAdapter<PARAM> buildAdapter() {
|
||||||
|
return new ValueAdapter<>(this.paramType)
|
||||||
|
.parser((holder, type, data) -> {
|
||||||
|
SOURCE source = holder.deserialize(this.sourceType, data);
|
||||||
|
return this.valueParser.handle(holder, source);
|
||||||
|
})
|
||||||
|
.serializer((holder, type, data) -> {
|
||||||
|
SOURCE source = this.valueSerializer.handle(holder, data);
|
||||||
|
return holder.serialize(source);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.list;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ConfigListBuilder<V> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<V> type;
|
||||||
|
|
||||||
|
public ConfigListBuilder(@NotNull ValueType<V> type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <S> @NotNull SourceListBuilder<S, V> from(@NotNull Class<S> sourceType) {
|
||||||
|
return from(ValueType.of(sourceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <S> @NotNull SourceListBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
|
||||||
|
return new SourceListBuilder<>(sourceType, type, ValueHandler.required(), ValueHandler.required(), ArrayList::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SourceListBuilder<String, V> fromString() {
|
||||||
|
return new SourceListBuilder<>(ValueType.STRING, type, ValueHandler.required(), ValueHandler.stringValue(), ArrayList::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SectionListBuilder<V> fromSection() {
|
||||||
|
return new SectionListBuilder<>(type, ValueHandler.required(), ValueHandler.required(), ArrayList::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.list;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class SectionListBuilder<V> extends AbstractSectionBuilder<List<V>, V, ConfiguredList<V>, SectionListBuilder<V>> {
|
||||||
|
|
||||||
|
protected @NotNull Supplier<? extends List<V>> constructor;
|
||||||
|
|
||||||
|
public SectionListBuilder(@NotNull ValueType<V> paramType,
|
||||||
|
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||||
|
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer,
|
||||||
|
@NotNull Supplier<? extends List<V>> constructor) {
|
||||||
|
super(new ValueType<List<V>>() {
|
||||||
|
}, paramType, parser, serializer);
|
||||||
|
this.constructor = constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public final @NotNull SectionListBuilder<V> defaults(@NotNull V... values) {
|
||||||
|
return defaults(new ArrayList<>(Arrays.asList(values)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final @NotNull SectionListBuilder<V> defaults(@NotNull Collection<V> values) {
|
||||||
|
return defaults(new ArrayList<>(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SectionListBuilder<V> constructor(@NotNull Supplier<? extends List<V>> constructor) {
|
||||||
|
this.constructor = constructor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <LIST extends List<V>> SectionListBuilder<V> construct(@NotNull LIST list) {
|
||||||
|
return constructor(() -> list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull SectionListBuilder<V> self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfiguredList<V> build() {
|
||||||
|
return new ConfiguredList<>(buildManifest(), constructor, buildAdapter());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.list;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class SourceListBuilder<SOURCE, V>
|
||||||
|
extends AbstractSourceBuilder<List<V>, SOURCE, V, ConfiguredList<V>, SourceListBuilder<SOURCE, V>> {
|
||||||
|
|
||||||
|
protected @NotNull Supplier<? extends List<V>> constructor;
|
||||||
|
|
||||||
|
public SourceListBuilder(@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<V> paramType,
|
||||||
|
@NotNull ValueHandler<SOURCE, V> parser, @NotNull ValueHandler<V, SOURCE> serializer,
|
||||||
|
@NotNull Supplier<? extends List<V>> constructor) {
|
||||||
|
super(new ValueType<List<V>>() {
|
||||||
|
}, sourceType, paramType, parser, serializer);
|
||||||
|
this.constructor = constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull V... values) {
|
||||||
|
return defaults(new ArrayList<>(Arrays.asList(values)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull Collection<V> values) {
|
||||||
|
return defaults(new ArrayList<>(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull SourceListBuilder<SOURCE, V> self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfiguredList<V> build() {
|
||||||
|
return new ConfiguredList<>(buildManifest(), this.constructor, buildAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.value;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ConfigValueBuilder<V> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<V> type;
|
||||||
|
|
||||||
|
public ConfigValueBuilder(@NotNull ValueType<V> type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull Class<S> clazz) {
|
||||||
|
return from(ValueType.of(clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
|
||||||
|
return from(sourceType, ValueHandler.required(), ValueHandler.required());
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType,
|
||||||
|
@NotNull ValueHandler<S, V> valueParser,
|
||||||
|
@NotNull ValueHandler<V, S> valueSerializer) {
|
||||||
|
return new SourceValueBuilder<>(sourceType, this.type, valueParser, valueSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SourceValueBuilder<String, V> fromString() {
|
||||||
|
return from(ValueType.STRING, ValueHandler.required(), ValueHandler.stringValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SectionValueBuilder<V> fromSection() {
|
||||||
|
return fromSection(ValueHandler.required(), ValueHandler.required());
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SectionValueBuilder<V> fromSection(
|
||||||
|
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||||
|
@NotNull ValueHandler<V, ? extends Map<String, Object>> valueSerializer
|
||||||
|
) {
|
||||||
|
return new SectionValueBuilder<>(this.type, valueParser, valueSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.value;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SectionValueBuilder<V> extends AbstractSectionBuilder<V, V, ConfiguredValue<V>, SectionValueBuilder<V>> {
|
||||||
|
|
||||||
|
public SectionValueBuilder(@NotNull ValueType<V> type,
|
||||||
|
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||||
|
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
|
||||||
|
super(type, type, parser, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull SectionValueBuilder<V> self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfiguredValue<V> build() {
|
||||||
|
return ConfiguredValue.of(buildManifest(), buildAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.value;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||||
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class SourceValueBuilder<S, V> extends AbstractSourceBuilder<V, S, V, ConfiguredValue<V>, SourceValueBuilder<S, V>> {
|
||||||
|
|
||||||
|
|
||||||
|
public SourceValueBuilder(@NotNull ValueType<S> sourceType, @NotNull ValueType<V> valueType,
|
||||||
|
@NotNull ValueHandler<S, V> parser, @NotNull ValueHandler<V, S> serializer) {
|
||||||
|
super(valueType, sourceType, valueType, parser, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull SourceValueBuilder<S, V> self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfiguredValue<V> build() {
|
||||||
|
return new ConfiguredValue<>(buildManifest(), buildAdapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,275 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
|
||||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
|
||||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置文件类初始化方法
|
|
||||||
* 用于初始化 {@link Configuration} 中的每个 {@link ConfigValue} 对象
|
|
||||||
*
|
|
||||||
* @param <T> {@link ConfigurationProvider} 配置文件的数据来源
|
|
||||||
* @author CarmJos
|
|
||||||
*/
|
|
||||||
public class ConfigInitializer<T extends ConfigurationProvider<?>> {
|
|
||||||
|
|
||||||
protected final @NotNull T provider;
|
|
||||||
|
|
||||||
public ConfigInitializer(@NotNull T provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Class<? extends Configuration> clazz, boolean saveDefaults) {
|
|
||||||
initialize(clazz, saveDefaults, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Class<? extends Configuration> clazz,
|
|
||||||
boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
initializeStaticClass(
|
|
||||||
clazz, null, null,
|
|
||||||
null, null, null,
|
|
||||||
saveDefaults, loadSubClasses
|
|
||||||
);
|
|
||||||
if (saveDefaults) {
|
|
||||||
try {
|
|
||||||
provider.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config) {
|
|
||||||
initialize(config, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config, boolean saveDefaults) {
|
|
||||||
initializeInstance(
|
|
||||||
config, null, null,
|
|
||||||
null, null, null,
|
|
||||||
saveDefaults
|
|
||||||
);
|
|
||||||
if (saveDefaults) {
|
|
||||||
try {
|
|
||||||
provider.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 针对实例类的初始化方法
|
|
||||||
private void initializeInstance(@NotNull Configuration root,
|
|
||||||
@Nullable String parentPath, @Nullable String fieldName,
|
|
||||||
@Nullable ConfigPath fieldPath,
|
|
||||||
@Nullable HeaderComment fieldHeaderComments,
|
|
||||||
@Nullable InlineComment fieldInlineComments,
|
|
||||||
boolean saveDefaults) {
|
|
||||||
String path = getClassPath(root.getClass(), parentPath, fieldName, fieldPath);
|
|
||||||
this.provider.setHeaderComment(path, getClassHeaderComments(root.getClass(), fieldHeaderComments));
|
|
||||||
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
|
|
||||||
|
|
||||||
for (Field field : root.getClass().getDeclaredFields()) {
|
|
||||||
initializeField(root, field, path, saveDefaults, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 针对静态类的初始化方法
|
|
||||||
private void initializeStaticClass(@NotNull Class<?> clazz,
|
|
||||||
@Nullable String parentPath, @Nullable String fieldName,
|
|
||||||
@Nullable ConfigPath fieldPath,
|
|
||||||
@Nullable HeaderComment fieldHeaderComments,
|
|
||||||
@Nullable InlineComment fieldInlineComments,
|
|
||||||
boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
if (!Configuration.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类
|
|
||||||
String path = getClassPath(clazz, parentPath, fieldName, fieldPath);
|
|
||||||
this.provider.setHeaderComment(path, getClassHeaderComments(clazz, fieldHeaderComments));
|
|
||||||
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
|
|
||||||
|
|
||||||
for (Field field : clazz.getDeclaredFields()) {
|
|
||||||
initializeField(clazz, field, path, saveDefaults, loadSubClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!loadSubClasses) return;
|
|
||||||
Class<?>[] classes = clazz.getDeclaredClasses();
|
|
||||||
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
|
||||||
initializeStaticClass(
|
|
||||||
classes[i], path, classes[i].getSimpleName(),
|
|
||||||
null, null, null,
|
|
||||||
saveDefaults, true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeField(@NotNull Object source, @NotNull Field field,
|
|
||||||
@Nullable String parent, boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
try {
|
|
||||||
field.setAccessible(true);
|
|
||||||
Object object = field.get(source);
|
|
||||||
|
|
||||||
if (object instanceof ConfigValue<?>) {
|
|
||||||
initializeValue(
|
|
||||||
(ConfigValue<?>) object, getFieldPath(field, parent),
|
|
||||||
field.getAnnotation(HeaderComment.class),
|
|
||||||
field.getAnnotation(InlineComment.class),
|
|
||||||
saveDefaults
|
|
||||||
);
|
|
||||||
} else if (source instanceof Configuration && object instanceof Configuration) {
|
|
||||||
// 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。
|
|
||||||
initializeInstance(
|
|
||||||
(Configuration) object, parent, field.getName(),
|
|
||||||
field.getAnnotation(ConfigPath.class),
|
|
||||||
field.getAnnotation(HeaderComment.class),
|
|
||||||
field.getAnnotation(InlineComment.class),
|
|
||||||
saveDefaults
|
|
||||||
);
|
|
||||||
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
|
||||||
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
|
||||||
initializeStaticClass(
|
|
||||||
(Class<?>) object, parent, field.getName(),
|
|
||||||
field.getAnnotation(ConfigPath.class),
|
|
||||||
field.getAnnotation(HeaderComment.class),
|
|
||||||
field.getAnnotation(InlineComment.class),
|
|
||||||
saveDefaults, loadSubClasses
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 以上判断实现以下规范:
|
|
||||||
// - 实例类中仅加载 ConfigValue实例 与 ConfigurationRoot实例
|
|
||||||
// - 静态类中仅加载 静态ConfigValue实例 与 静态ConfigurationRoot类
|
|
||||||
|
|
||||||
} catch (IllegalAccessException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initializeValue(@NotNull ConfigValue<?> value, @NotNull String path,
|
|
||||||
@Nullable HeaderComment fieldHeaderComment,
|
|
||||||
@Nullable InlineComment fieldInlineComment,
|
|
||||||
boolean saveDefaults) {
|
|
||||||
value.initialize(
|
|
||||||
provider, saveDefaults, path,
|
|
||||||
readHeaderComments(fieldHeaderComment),
|
|
||||||
readInlineComments(fieldInlineComment)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static @Nullable List<String> getClassHeaderComments(@NotNull Class<?> clazz,
|
|
||||||
@Nullable HeaderComment fieldAnnotation) {
|
|
||||||
List<String> classComments = readHeaderComments(clazz.getAnnotation(HeaderComment.class));
|
|
||||||
if (classComments != null) return classComments;
|
|
||||||
else return readHeaderComments(fieldAnnotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected static List<String> readHeaderComments(@Nullable HeaderComment annotation) {
|
|
||||||
if (annotation == null) return null;
|
|
||||||
String[] value = annotation.value();
|
|
||||||
return value.length > 0 ? Arrays.asList(value) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected static @Nullable String readInlineComments(@Nullable InlineComment annotation) {
|
|
||||||
if (annotation == null) return null;
|
|
||||||
String value = annotation.value();
|
|
||||||
return value.length() > 0 ? value : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static @Nullable String getClassPath(@Nullable Class<?> clazz,
|
|
||||||
@Nullable String parentPath,
|
|
||||||
@Nullable String filedName,
|
|
||||||
@Nullable ConfigPath fieldAnnotation) {
|
|
||||||
@NotNull String parent = parentPath != null ? parentPath + "." : "";
|
|
||||||
boolean fromRoot = false;
|
|
||||||
|
|
||||||
// 先获取 Class 对应的路径注解 其优先度最高。
|
|
||||||
if (clazz != null) {
|
|
||||||
ConfigPath clazzAnnotation = clazz.getAnnotation(ConfigPath.class);
|
|
||||||
if (clazzAnnotation != null) {
|
|
||||||
fromRoot = clazzAnnotation.root();
|
|
||||||
if (clazzAnnotation.value().length() > 0) {
|
|
||||||
return (fromRoot ? "" : parent) + clazzAnnotation.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fieldAnnotation != null) {
|
|
||||||
fromRoot = fromRoot || fieldAnnotation.root();
|
|
||||||
if (fieldAnnotation.value().length() > 0) {
|
|
||||||
return (fromRoot ? "" : parent) + fieldAnnotation.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 再由 fieldName 获取路径
|
|
||||||
if (filedName != null) return (fromRoot ? "" : parent) + getPathFromName(filedName);
|
|
||||||
else return null; // 不满足上述条件 且 无 fieldName,则说明是根路径。
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static @NotNull String getFieldPath(@NotNull Field field, @Nullable String parentPath) {
|
|
||||||
@NotNull String parent = parentPath != null ? parentPath + "." : "";
|
|
||||||
boolean fromRoot = false;
|
|
||||||
|
|
||||||
// 先获取 Field 对应的路径注解 其优先度最高。
|
|
||||||
ConfigPath pathAnnotation = field.getAnnotation(ConfigPath.class);
|
|
||||||
if (pathAnnotation != null) {
|
|
||||||
fromRoot = pathAnnotation.root();
|
|
||||||
if (pathAnnotation.value().length() > 0) {
|
|
||||||
return (fromRoot ? "" : parent) + pathAnnotation.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最后再通过 fieldName 自动生成路径
|
|
||||||
return (fromRoot ? "" : parent) + getPathFromName(field.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到指定元素的配置名称。
|
|
||||||
* 采用 全小写,以“-”链接 的命名规则。
|
|
||||||
*
|
|
||||||
* @param name 源名称
|
|
||||||
* @return 全小写,以“-”链接 的 路径名称
|
|
||||||
*/
|
|
||||||
public static String getPathFromName(String name) {
|
|
||||||
return name.replaceAll("[A-Z]", "-$0") // 将驼峰转换为蛇形;
|
|
||||||
.replaceAll("-(.*)", "$1") // 若首字母也为大写,则也会被转换,需要去掉第一个横线
|
|
||||||
.replaceAll("_-([A-Z])", "_$1") // 因为命名中可能包含 _,因此需要被特殊处理一下
|
|
||||||
.replaceAll("([a-z])-([A-Z])", "$1_$2") // 然后将非全大写命名的内容进行转换
|
|
||||||
.replace("-", "") // 移除掉多余的横线
|
|
||||||
.replace("_", "-") // 将下划线替换为横线
|
|
||||||
.toLowerCase(); // 最后转为全小写
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root interface of the configuration file interfaces,
|
|
||||||
* which is used to label and record the configuration information.
|
|
||||||
*/
|
|
||||||
public interface Configuration {
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root node of the configuration file class,
|
|
||||||
* which is used to label and record the configuration information.
|
|
||||||
*/
|
|
||||||
public abstract class ConfigurationRoot implements Configuration {
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T, B, P>, P extends ConfigurationProvider<?>> {
|
|
||||||
|
|
||||||
protected final Class<? super P> providerClass;
|
|
||||||
|
|
||||||
protected @Nullable P provider;
|
|
||||||
protected @Nullable String path;
|
|
||||||
|
|
||||||
protected @Nullable List<String> headerComments;
|
|
||||||
protected @Nullable String inlineComment;
|
|
||||||
|
|
||||||
protected @Nullable T defaultValue;
|
|
||||||
|
|
||||||
protected AbstractConfigBuilder(Class<? super P> providerClass) {
|
|
||||||
this.providerClass = providerClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract @NotNull B getThis();
|
|
||||||
|
|
||||||
public abstract @NotNull ConfigValue<?> build();
|
|
||||||
|
|
||||||
public @NotNull B from(@Nullable P provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
return getThis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull B path(@Nullable String path) {
|
|
||||||
this.path = path;
|
|
||||||
return getThis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull B comments(@NotNull String... comments) {
|
|
||||||
return headerComments(comments);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull B headerComments(@NotNull String... comments) {
|
|
||||||
return headerComments(Arrays.asList(comments));
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull B headerComments(@NotNull List<String> comments) {
|
|
||||||
this.headerComments = comments;
|
|
||||||
return getThis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull B inlineComment(@NotNull String comment) {
|
|
||||||
this.inlineComment = comment;
|
|
||||||
return getThis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull B defaults(@Nullable T defaultValue) {
|
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
return getThis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull B defaults(@NotNull Supplier<@Nullable T> defaultValueSupplier) {
|
|
||||||
return defaults(defaultValueSupplier.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @NotNull ValueManifest<T> buildManifest() {
|
|
||||||
return ValueManifest.of(
|
|
||||||
this.provider, this.path,
|
|
||||||
this.headerComments, this.inlineComment, this.defaultValue
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
|
|
||||||
public abstract class CommonConfigBuilder<T, B extends CommonConfigBuilder<T, B>>
|
|
||||||
extends AbstractConfigBuilder<T, B, ConfigurationProvider<?>> {
|
|
||||||
|
|
||||||
protected CommonConfigBuilder() {
|
|
||||||
super(ConfigurationProvider.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapCreator;
|
|
||||||
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
public class ConfigBuilder {
|
|
||||||
|
|
||||||
public <V> @NotNull ConfigValueBuilder<V> asValue(@NotNull Class<V> valueClass) {
|
|
||||||
return new ConfigValueBuilder<>(valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <V> @NotNull ConfigListBuilder<V> asList(@NotNull Class<V> valueClass) {
|
|
||||||
return new ConfigListBuilder<>(valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <K, V> @NotNull ConfigMapCreator<K, V> asMap(@NotNull Class<K> keyClass,
|
|
||||||
@NotNull Class<V> valueClass) {
|
|
||||||
return new ConfigMapCreator<>(keyClass, valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <K, V> @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap(@NotNull Class<K> keyClass,
|
|
||||||
@NotNull Class<V> valueClass) {
|
|
||||||
return asMap(keyClass, valueClass).asHashMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap(@NotNull Class<K> keyClass,
|
|
||||||
@NotNull Class<V> valueClass) {
|
|
||||||
return asMap(keyClass, valueClass).asLinkedMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public <K extends Comparable<K>, V> @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Class<K> keyClass,
|
|
||||||
@NotNull Class<V> valueClass) {
|
|
||||||
return asMap(keyClass, valueClass).asTreeMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.list;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
public class ConfigListBuilder<V> {
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
public ConfigListBuilder(@NotNull Class<V> valueClass) {
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull <S> SourceListBuilder<S, V> from(@NotNull Class<? super S> sourceClass,
|
|
||||||
@NotNull ConfigDataFunction<Object, S> sourceParser,
|
|
||||||
@NotNull ConfigDataFunction<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
|
||||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
|
||||||
return new SourceListBuilder<>(sourceClass, sourceParser, this.valueClass, valueParser, valueSerializer, sourceSerializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public @NotNull <S> SourceListBuilder<S, V> from(Class<S> sourceClass) {
|
|
||||||
return from(sourceClass,
|
|
||||||
ConfigDataFunction.required(), ConfigDataFunction.required(),
|
|
||||||
ConfigDataFunction.required(), ConfigDataFunction.required()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<Object, V> fromObject() {
|
|
||||||
return from(
|
|
||||||
Object.class, ConfigDataFunction.identity(),
|
|
||||||
ConfigDataFunction.castObject(valueClass),
|
|
||||||
ConfigDataFunction.toObject(), ConfigDataFunction.toObject()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<String, V> fromString() {
|
|
||||||
return from(
|
|
||||||
String.class, ConfigDataFunction.castToString(),
|
|
||||||
ConfigDataFunction.castFromString(this.valueClass),
|
|
||||||
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<Map<String, Object>, V> fromMap() {
|
|
||||||
return from(
|
|
||||||
Map.class, obj -> (Map<String, Object>) obj,
|
|
||||||
ConfigDataFunction.required(),
|
|
||||||
ConfigDataFunction.required(),
|
|
||||||
ConfigDataFunction.toObject()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.list;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SourceListBuilder<S, V> extends CommonConfigBuilder<List<V>, SourceListBuilder<S, V>> {
|
|
||||||
|
|
||||||
protected final @NotNull Class<? super S> sourceClass;
|
|
||||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
protected @NotNull ConfigDataFunction<S, V> valueParser;
|
|
||||||
|
|
||||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
|
||||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
|
||||||
|
|
||||||
public SourceListBuilder(@NotNull Class<? super S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
|
||||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
|
||||||
this.sourceClass = sourceClass;
|
|
||||||
this.sourceParser = sourceParser;
|
|
||||||
this.sourceSerializer = sourceSerializer;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
this.valueSerializer = valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SafeVarargs
|
|
||||||
public final @NotNull SourceListBuilder<S, V> defaults(@NotNull V... values) {
|
|
||||||
return defaults(new ArrayList<>(Arrays.asList(values)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final @NotNull SourceListBuilder<S, V> defaults(@NotNull Collection<V> values) {
|
|
||||||
return defaults(new ArrayList<>(values));
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<S, V> parseSource(ConfigDataFunction<Object, S> sourceParser) {
|
|
||||||
this.sourceParser = sourceParser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<S, V> parseValue(ConfigDataFunction<S, V> valueParser) {
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<S, V> serializeValue(ConfigDataFunction<V, S> serializer) {
|
|
||||||
this.valueSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<S, V> serializeSource(ConfigDataFunction<S, Object> serializer) {
|
|
||||||
this.sourceSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull SourceListBuilder<S, V> getThis() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfiguredList<V> build() {
|
|
||||||
return new ConfiguredList<>(
|
|
||||||
buildManifest(), this.valueClass,
|
|
||||||
this.sourceParser.andThen(this.valueParser),
|
|
||||||
this.valueSerializer.andThen(sourceSerializer)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.map;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
|
|
||||||
|
|
||||||
protected final @NotNull Supplier<? extends M> supplier;
|
|
||||||
|
|
||||||
protected final @NotNull Class<K> keyClass;
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
public ConfigMapBuilder(@NotNull Supplier<? extends M> supplier, @NotNull Class<K> keyClass, @NotNull Class<V> valueClass) {
|
|
||||||
this.supplier = supplier;
|
|
||||||
this.keyClass = keyClass;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <MAP extends Map<K, V>> ConfigMapBuilder<MAP, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
|
||||||
return new ConfigMapBuilder<>(supplier, keyClass, valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass,
|
|
||||||
@NotNull ConfigDataFunction<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<V, S> valueSerializer) {
|
|
||||||
return new SourceMapBuilder<>(supplier,
|
|
||||||
keyClass, ConfigDataFunction.castFromString(this.keyClass), // #String -> key
|
|
||||||
sourceClass, ConfigDataFunction.castObject(sourceClass), // #Object -> source
|
|
||||||
valueClass, valueParser, // source -> value
|
|
||||||
ConfigDataFunction.castToString(), // key -> #String
|
|
||||||
valueSerializer/*value -> source*/,
|
|
||||||
ConfigDataFunction.toObject()/* source -> #Object */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass) {
|
|
||||||
return from(sourceClass, ConfigDataFunction.required(), ConfigDataFunction.required());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceMapBuilder<M, String, K, V> fromString(@NotNull ConfigDataFunction<String, V> valueParser) {
|
|
||||||
return from(String.class, valueParser, ConfigDataFunction.castToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceMapBuilder<M, String, K, V> fromString() {
|
|
||||||
return fromString(ConfigDataFunction.castFromString(this.valueClass));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SectionMapBuilder<M, K, V> fromSection() {
|
|
||||||
return new SectionMapBuilder<>(
|
|
||||||
supplier,
|
|
||||||
keyClass, ConfigDataFunction.castFromString(keyClass),
|
|
||||||
valueClass, ConfigDataFunction.required(),
|
|
||||||
ConfigDataFunction.castToString(), ConfigDataFunction.required());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceMapBuilder<M, Object, K, V> fromObject(@NotNull ConfigDataFunction<Object, V> valueParser) {
|
|
||||||
return from(Object.class, valueParser, ConfigDataFunction.toObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
public SourceMapBuilder<M, Object, K, V> fromObject() {
|
|
||||||
return fromObject(ConfigDataFunction.required());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.map;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ConfigMapCreator<K, V> {
|
|
||||||
|
|
||||||
protected final @NotNull Class<K> keyClass;
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
public ConfigMapCreator(@NotNull Class<K> keyClass, @NotNull Class<V> valueClass) {
|
|
||||||
this.keyClass = keyClass;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <M extends Map<K, V>> @NotNull ConfigMapBuilder<M, K, V> asMap(Supplier<? extends M> mapSuppler) {
|
|
||||||
return new ConfigMapBuilder<>(mapSuppler, keyClass, valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap() {
|
|
||||||
return asMap(HashMap::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap() {
|
|
||||||
return asMap(LinkedHashMap::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap() {
|
|
||||||
return asMap(TreeMap::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Comparator<? super K> comparator) {
|
|
||||||
return asMap(() -> new TreeMap<>(comparator));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.map;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSectionMap;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class SectionMapBuilder<M extends Map<K, V>, K, V> extends CommonConfigBuilder<M, SectionMapBuilder<M, K, V>> {
|
|
||||||
|
|
||||||
protected final @NotNull Supplier<? extends M> supplier;
|
|
||||||
|
|
||||||
protected final @NotNull Class<K> keyClass;
|
|
||||||
protected @NotNull ConfigDataFunction<String, K> keyParser;
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
protected @NotNull ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser;
|
|
||||||
|
|
||||||
protected @NotNull ConfigDataFunction<K, String> keySerializer;
|
|
||||||
protected @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer;
|
|
||||||
|
|
||||||
public SectionMapBuilder(@NotNull Supplier<? extends M> supplier,
|
|
||||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
|
|
||||||
this.supplier = supplier;
|
|
||||||
this.keyClass = keyClass;
|
|
||||||
this.keyParser = keyParser;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
this.keySerializer = keySerializer;
|
|
||||||
this.valueSerializer = valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <MAP extends Map<K, V>> SectionMapBuilder<MAP, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
|
||||||
return new SectionMapBuilder<>(supplier,
|
|
||||||
keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> defaults(@NotNull Consumer<M> factory) {
|
|
||||||
M map = supplier.get();
|
|
||||||
factory.accept(map);
|
|
||||||
return defaults(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> parseKey(@NotNull ConfigDataFunction<String, K> parser) {
|
|
||||||
this.keyParser = parser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> parseValue(@NotNull ConfigDataFunction<ConfigurationWrapper<?>, V> parser) {
|
|
||||||
this.valueParser = parser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> serializeKey(@NotNull ConfigDataFunction<K, String> serializer) {
|
|
||||||
this.keySerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> serializeValue(@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
|
||||||
this.valueSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> serializeValue(@NotNull BiConsumer<V, Map<String, Object>> serializer) {
|
|
||||||
return serializeValue(v -> {
|
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
|
||||||
serializer.accept(v, map);
|
|
||||||
return map;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull SectionMapBuilder<M, K, V> getThis() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfiguredSectionMap<K, V> build() {
|
|
||||||
return new ConfiguredSectionMap<>(
|
|
||||||
new ValueManifest<>(provider, path, headerComments, inlineComment, defaultValue),
|
|
||||||
this.supplier, this.keyClass, this.keyParser,
|
|
||||||
this.valueClass, this.valueParser,
|
|
||||||
this.keySerializer, this.valueSerializer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.map;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class SourceMapBuilder<M extends Map<K, V>, S, K, V> extends CommonConfigBuilder<M, SourceMapBuilder<M, S, K, V>> {
|
|
||||||
|
|
||||||
protected final @NotNull Supplier<? extends M> supplier;
|
|
||||||
|
|
||||||
protected final @NotNull Class<K> keyClass;
|
|
||||||
protected @NotNull ConfigDataFunction<String, K> keyParser;
|
|
||||||
|
|
||||||
protected final @NotNull Class<S> sourceClass;
|
|
||||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
protected @NotNull ConfigDataFunction<S, V> valueParser;
|
|
||||||
|
|
||||||
protected @NotNull ConfigDataFunction<K, String> keySerializer;
|
|
||||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
|
||||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
|
||||||
|
|
||||||
public SourceMapBuilder(@NotNull Supplier<? extends M> supplier,
|
|
||||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
|
||||||
@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
|
||||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
|
||||||
this.supplier = supplier;
|
|
||||||
this.keyClass = keyClass;
|
|
||||||
this.keyParser = keyParser;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
this.sourceClass = sourceClass;
|
|
||||||
this.sourceParser = sourceParser;
|
|
||||||
this.keySerializer = keySerializer;
|
|
||||||
this.valueSerializer = valueSerializer;
|
|
||||||
this.sourceSerializer = sourceSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <MAP extends Map<K, V>> SourceMapBuilder<MAP, S, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
|
||||||
return new SourceMapBuilder<>(supplier,
|
|
||||||
keyClass, keyParser, sourceClass, sourceParser, valueClass, valueParser,
|
|
||||||
keySerializer, valueSerializer, sourceSerializer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceMapBuilder<M, S, K, V> defaults(@NotNull Consumer<M> factory) {
|
|
||||||
M map = supplier.get();
|
|
||||||
factory.accept(map);
|
|
||||||
return defaults(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceMapBuilder<M, S, K, V> parseKey(@NotNull ConfigDataFunction<String, K> parser) {
|
|
||||||
this.keyParser = parser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceMapBuilder<M, S, K, V> parseSource(@NotNull ConfigDataFunction<Object, S> parser) {
|
|
||||||
this.sourceParser = parser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceMapBuilder<M, S, K, V> parseValue(@NotNull ConfigDataFunction<S, V> parser) {
|
|
||||||
this.valueParser = parser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeKey(@NotNull ConfigDataFunction<K, String> serializer) {
|
|
||||||
this.keySerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
|
|
||||||
this.valueSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
|
|
||||||
this.sourceSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull SourceMapBuilder<M, S, K, V> getThis() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfiguredMap<K, V> build() {
|
|
||||||
return new ConfiguredMap<>(
|
|
||||||
new ValueManifest<>(provider, path, headerComments, inlineComment, defaultValue),
|
|
||||||
this.supplier, this.keyClass, this.keyParser,
|
|
||||||
this.valueClass, this.sourceParser.andThen(this.valueParser),
|
|
||||||
this.keySerializer, this.valueSerializer.andThen(this.sourceSerializer)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-64
@@ -1,64 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.value;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ConfigValueBuilder<V> {
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
public ConfigValueBuilder(@NotNull Class<V> valueClass) {
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionValueBuilder<V> fromSection() {
|
|
||||||
return fromSection(ConfigValueParser.required(), ConfigDataFunction.required());
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionValueBuilder<V> fromSection(@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
|
|
||||||
return new SectionValueBuilder<>(this.valueClass, valueParser, valueSerializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull <S> SourceValueBuilder<S, V> from(Class<S> sourceClass) {
|
|
||||||
return from(
|
|
||||||
sourceClass, ConfigDataFunction.required(), ConfigValueParser.required(),
|
|
||||||
ConfigDataFunction.required(), ConfigDataFunction.required()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull Class<S> sourceClass,
|
|
||||||
@NotNull ConfigDataFunction<Object, S> sourceParser,
|
|
||||||
@NotNull ConfigValueParser<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
|
||||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
|
||||||
return new SourceValueBuilder<>(
|
|
||||||
sourceClass, sourceParser, this.valueClass, valueParser,
|
|
||||||
valueSerializer, sourceSerializer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<Object, V> fromObject() {
|
|
||||||
return from(
|
|
||||||
Object.class, ConfigDataFunction.identity(),
|
|
||||||
ConfigValueParser.castObject(valueClass),
|
|
||||||
s -> {
|
|
||||||
if (s instanceof Enum<?>) return ((Enum<?>) s).name();
|
|
||||||
else return s;
|
|
||||||
}, ConfigDataFunction.toObject()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<String, V> fromString() {
|
|
||||||
return from(
|
|
||||||
String.class, ConfigDataFunction.castToString(),
|
|
||||||
ConfigValueParser.parseString(this.valueClass),
|
|
||||||
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.value;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class SectionValueBuilder<V>
|
|
||||||
extends CommonConfigBuilder<V, SectionValueBuilder<V>> {
|
|
||||||
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
protected @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser;
|
|
||||||
protected @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
|
|
||||||
|
|
||||||
public SectionValueBuilder(@NotNull Class<V> valueClass,
|
|
||||||
@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser,
|
|
||||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.parser = parser;
|
|
||||||
this.serializer = serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull SectionValueBuilder<V> getThis() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionValueBuilder<V> parseValue(ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser) {
|
|
||||||
return parseValue((section, path) -> valueParser.parse(section));
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper<?>, V> valueParser) {
|
|
||||||
this.parser = valueParser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionValueBuilder<V> serializeValue(ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
|
||||||
this.serializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfiguredSection<V> build() {
|
|
||||||
return new ConfiguredSection<>(buildManifest(), this.valueClass, this.parser, this.serializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-70
@@ -1,70 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.value;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SourceValueBuilder<S, V> extends CommonConfigBuilder<V, SourceValueBuilder<S, V>> {
|
|
||||||
|
|
||||||
protected final @NotNull Class<S> sourceClass;
|
|
||||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
protected @NotNull ConfigValueParser<S, V> valueParser;
|
|
||||||
|
|
||||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
|
||||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
|
||||||
|
|
||||||
public SourceValueBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigValueParser<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
|
||||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
|
||||||
this.sourceClass = sourceClass;
|
|
||||||
this.sourceParser = sourceParser;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
this.sourceSerializer = sourceSerializer;
|
|
||||||
this.valueSerializer = valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull SourceValueBuilder<S, V> getThis() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<S, V> parseSource(@NotNull ConfigDataFunction<Object, S> sourceParser) {
|
|
||||||
this.sourceParser = sourceParser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<S, V> parseValue(ConfigDataFunction<S, V> valueParser) {
|
|
||||||
return parseValue((section, path) -> valueParser.parse(section));
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<S, V> parseValue(@NotNull ConfigValueParser<S, V> valueParser) {
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<S, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
|
|
||||||
this.valueSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<S, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
|
|
||||||
this.sourceSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfiguredValue<V> build() {
|
|
||||||
return new ConfiguredValue<>(
|
|
||||||
buildManifest(), this.valueClass,
|
|
||||||
this.valueParser.compose(this.sourceParser),
|
|
||||||
this.valueSerializer.andThen(sourceSerializer)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.function;
|
|
||||||
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface ConfigValueParser<T, R> {
|
|
||||||
|
|
||||||
@Nullable R parse(@NotNull T data, @Nullable R defaultValue) throws Exception;
|
|
||||||
|
|
||||||
default <V> ConfigValueParser<T, V> andThen(@NotNull ConfigValueParser<R, V> after) {
|
|
||||||
Objects.requireNonNull(after);
|
|
||||||
return ((data, defaultValue) -> {
|
|
||||||
R result = parse(data, null);
|
|
||||||
if (result == null) return defaultValue;
|
|
||||||
else return after.parse(result, defaultValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
default <V> ConfigValueParser<V, R> compose(@NotNull ConfigDataFunction<? super V, ? extends T> before) {
|
|
||||||
Objects.requireNonNull(before);
|
|
||||||
return ((data, defaultValue) -> {
|
|
||||||
T result = before.parse(data);
|
|
||||||
return parse(result, defaultValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static <T> @NotNull ConfigValueParser<T, T> identity() {
|
|
||||||
return (input, defaultValue) -> input;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static <T> @NotNull ConfigValueParser<T, Object> toObject() {
|
|
||||||
return (input, defaultValue) -> input;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static <T, V> @NotNull ConfigValueParser<T, V> required() {
|
|
||||||
return (input, defaultValue) -> {
|
|
||||||
throw new IllegalArgumentException("Please specify the value parser.");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static <V> @NotNull ConfigValueParser<Object, V> castObject(Class<V> valueClass) {
|
|
||||||
if (Number.class.isAssignableFrom(valueClass)) return castNumber(valueClass);
|
|
||||||
else return (input, defaultValue) -> {
|
|
||||||
if (Boolean.class.isAssignableFrom(valueClass) || boolean.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = booleanValue().parse(input, (Boolean) defaultValue);
|
|
||||||
} else if (Enum.class.isAssignableFrom(valueClass) && input instanceof String) {
|
|
||||||
String enumName = (String) input;
|
|
||||||
input = valueClass.getDeclaredMethod("valueOf", String.class).invoke(null, enumName);
|
|
||||||
} else if (UUID.class.isAssignableFrom(valueClass) && input instanceof String) {
|
|
||||||
input = UUID.fromString((String) input);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
|
||||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static <V> @NotNull ConfigValueParser<Object, V> castNumber(Class<V> valueClass) {
|
|
||||||
return (input, defaultValue) -> {
|
|
||||||
if (Long.class.isAssignableFrom(valueClass) || long.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = longValue().parse(input, (Long) defaultValue);
|
|
||||||
} else if (Integer.class.isAssignableFrom(valueClass) || int.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = intValue().parse(input, (Integer) defaultValue);
|
|
||||||
} else if (Float.class.isAssignableFrom(valueClass) || float.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = floatValue().parse(input, (Float) defaultValue);
|
|
||||||
} else if (Double.class.isAssignableFrom(valueClass) || double.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = doubleValue().parse(input, (Double) defaultValue);
|
|
||||||
} else if (Byte.class.isAssignableFrom(valueClass) || byte.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = byteValue().parse(input, (Byte) defaultValue);
|
|
||||||
} else if (Short.class.isAssignableFrom(valueClass) || short.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = shortValue().parse(input, (Short) defaultValue);
|
|
||||||
}
|
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
|
||||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static <V> @NotNull ConfigValueParser<String, V> parseString(Class<V> valueClass) {
|
|
||||||
return (input, defaultValue) -> {
|
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
|
||||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, String> castToString() {
|
|
||||||
return (input, defaultValue) -> {
|
|
||||||
if (input instanceof String) return (String) input;
|
|
||||||
else return input.toString();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, Integer> intValue() {
|
|
||||||
return (input, defaultValue) -> ConfigDataFunction.intValue().parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, Short> shortValue() {
|
|
||||||
return (input, defaultValue) -> ConfigDataFunction.shortValue().parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, Double> doubleValue() {
|
|
||||||
return (input, defaultValue) -> ConfigDataFunction.doubleValue().parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, Byte> byteValue() {
|
|
||||||
return (input, defaultValue) -> ConfigDataFunction.byteValue().parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, Float> floatValue() {
|
|
||||||
return (input, defaultValue) -> ConfigDataFunction.floatValue().parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, Long> longValue() {
|
|
||||||
return (input, defaultValue) -> ConfigDataFunction.longValue().parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull ConfigValueParser<Object, Boolean> booleanValue() {
|
|
||||||
return (input, defaultValue) -> ConfigDataFunction.booleanValue().parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull <E extends Enum<E>> ConfigValueParser<Object, E> enumValue(Class<E> enumClass) {
|
|
||||||
return (input, defaultValue) -> {
|
|
||||||
if (input instanceof Enum) {
|
|
||||||
return enumClass.cast(input);
|
|
||||||
} else if (input instanceof String) {
|
|
||||||
return Enum.valueOf(enumClass, (String) input);
|
|
||||||
} else if (input instanceof Number) {
|
|
||||||
return enumClass.getEnumConstants()[((Number) input).intValue()];
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Cannot cast value to " + enumClass.getName());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.source;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class ConfigurationComments {
|
|
||||||
|
|
||||||
protected final @NotNull Map<String, List<String>> headerComments = new HashMap<>();
|
|
||||||
protected final @NotNull Map<String, String> inlineComments = new HashMap<>();
|
|
||||||
|
|
||||||
protected @NotNull Map<String, List<String>> getHeaderComments() {
|
|
||||||
return headerComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @NotNull Map<String, String> getInlineComments() {
|
|
||||||
return inlineComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHeaderComments(@Nullable String path, @Nullable List<String> comments) {
|
|
||||||
|
|
||||||
if (comments == null) {
|
|
||||||
getHeaderComments().remove(path);
|
|
||||||
} else {
|
|
||||||
getHeaderComments().put(path, comments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setInlineComment(@NotNull String path, @Nullable String comment) {
|
|
||||||
if (comment == null) {
|
|
||||||
getInlineComments().remove(path);
|
|
||||||
} else {
|
|
||||||
getInlineComments().put(path, comment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Unmodifiable
|
|
||||||
public List<String> getHeaderComment(@Nullable String path) {
|
|
||||||
return Optional.ofNullable(getHeaderComments().get(path)).map(Collections::unmodifiableList).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String getInlineComment(@NotNull String path) {
|
|
||||||
return getInlineComments().get(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.source;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
import cc.carm.lib.configuration.core.Configuration;
|
|
||||||
import cc.carm.lib.configuration.core.Configuration;
|
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
|
||||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置文件提供者,用于为 {@link ConfigValue} 提供配置文件的源,以便实现读取、保存等操作。
|
|
||||||
*
|
|
||||||
* @param <W> 配置文件的原生功能类
|
|
||||||
*/
|
|
||||||
public abstract class ConfigurationProvider<W extends ConfigurationWrapper<?>> {
|
|
||||||
|
|
||||||
protected long updateTime;
|
|
||||||
|
|
||||||
protected ConfigurationProvider() {
|
|
||||||
this.updateTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到配置文件的更新(最后加载)时间。
|
|
||||||
*
|
|
||||||
* @return 更新时间
|
|
||||||
*/
|
|
||||||
public long getUpdateTime() {
|
|
||||||
return updateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用于 {@link CachedConfigValue} 判断缓存值是否过期(即缓存的时间早于配置文件的最后加载时间)。
|
|
||||||
*
|
|
||||||
* @param time 缓存值时的时间戳
|
|
||||||
* @return 缓存值是否过期
|
|
||||||
*/
|
|
||||||
public boolean isExpired(long time) {
|
|
||||||
return getUpdateTime() > time;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到配置文件的原生功能类。
|
|
||||||
*
|
|
||||||
* @return 原生类
|
|
||||||
*/
|
|
||||||
public abstract @NotNull W getConfiguration();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重载当前配置文件。(将不会保存已修改的内容)
|
|
||||||
*
|
|
||||||
* @throws Exception 当重载出现错误时抛出
|
|
||||||
*/
|
|
||||||
public void reload() throws Exception {
|
|
||||||
onReload(); // 调用重写的Reload方法
|
|
||||||
this.updateTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将当前对配置文件的更改进行保存。
|
|
||||||
*
|
|
||||||
* @throws Exception 当保存出现错误时抛出
|
|
||||||
*/
|
|
||||||
public abstract void save() throws Exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 针对于不同配置文件类型所执行的重载操作。
|
|
||||||
*
|
|
||||||
* @throws Exception 当操作出现错误时抛出。
|
|
||||||
*/
|
|
||||||
protected abstract void onReload() throws Exception;
|
|
||||||
|
|
||||||
public abstract @Nullable ConfigurationComments getComments();
|
|
||||||
|
|
||||||
public void setHeaderComment(@Nullable String path, @Nullable List<String> comments) {
|
|
||||||
if (getComments() == null) return;
|
|
||||||
getComments().setHeaderComments(path, comments);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInlineComment(@NotNull String path, @Nullable String comment) {
|
|
||||||
if (getComments() == null) return;
|
|
||||||
getComments().setInlineComment(path, comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Unmodifiable
|
|
||||||
public List<String> getHeaderComment(@Nullable String path) {
|
|
||||||
return Optional.ofNullable(getComments()).map(c -> c.getHeaderComment(path)).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String getInlineComment(@NotNull String path) {
|
|
||||||
return Optional.ofNullable(getComments()).map(c -> c.getInlineComment(path)).orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract @NotNull ConfigInitializer<? extends ConfigurationProvider<W>> getInitializer();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param configClazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
*/
|
|
||||||
public void initialize(Class<? extends Configuration> configClazz) {
|
|
||||||
initialize(configClazz, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param configClazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(Class<? extends Configuration> configClazz, boolean saveDefaults) {
|
|
||||||
this.getInitializer().initialize(configClazz, saveDefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param configClazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(Class<? extends Configuration> configClazz, boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config) {
|
|
||||||
this.getInitializer().initialize(config, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config, boolean saveDefaults) {
|
|
||||||
this.getInitializer().initialize(config, saveDefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.source;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
interface ConfigurationReader {
|
|
||||||
|
|
||||||
ConfigurationWrapper<?> getWrapper();
|
|
||||||
|
|
||||||
default boolean isBoolean(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Boolean.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean getBoolean(@NotNull String path) {
|
|
||||||
return getBoolean(path, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
|
||||||
return getWrapper().get(path, def, ConfigValueParser.booleanValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Boolean isByte(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Byte.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Byte getByte(@NotNull String path) {
|
|
||||||
return getByte(path, (byte) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
|
||||||
return getWrapper().get(path, def, ConfigValueParser.byteValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean isShort(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Short.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Short getShort(@NotNull String path) {
|
|
||||||
return getShort(path, (short) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
|
||||||
return getWrapper().get(path, def, ConfigValueParser.shortValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default boolean isInt(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Integer.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Integer getInt(@NotNull String path) {
|
|
||||||
return getInt(path, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
|
||||||
return getWrapper().get(path, def, ConfigValueParser.intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default boolean isLong(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Long.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Long getLong(@NotNull String path) {
|
|
||||||
return getLong(path, 0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
|
||||||
return getWrapper().get(path, def, ConfigValueParser.longValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default boolean isFloat(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Float.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Float getFloat(@NotNull String path) {
|
|
||||||
return getFloat(path, 0.0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
|
||||||
return getWrapper().get(path, def, ConfigValueParser.floatValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default boolean isDouble(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Double.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Double getDouble(@NotNull String path) {
|
|
||||||
return getDouble(path, 0.0D);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
|
||||||
return getWrapper().get(path, def, ConfigValueParser.doubleValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default boolean isChar(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, Boolean.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable Character getChar(@NotNull String path) {
|
|
||||||
return getChar(path, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
|
||||||
return getWrapper().get(path, def, Character.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
default boolean isString(@NotNull String path) {
|
|
||||||
return getWrapper().isType(path, String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable String getString(@NotNull String path) {
|
|
||||||
return getString(path, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_, !null -> !null")
|
|
||||||
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
|
||||||
return getWrapper().get(path, def, String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
default <V> @NotNull List<V> getList(@NotNull String path, @NotNull ConfigValueParser<Object, V> parser) {
|
|
||||||
return parseList(getWrapper().getList(path), parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<String> getStringList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.castToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Long> getLongList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.longValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Double> getDoubleList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.doubleValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Float> getFloatList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.floatValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Byte> getByteList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.byteValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Character> getCharList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.castObject(Character.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
static <T> @NotNull List<T> parseList(@Nullable List<?> list, ConfigValueParser<Object, T> parser) {
|
|
||||||
if (list == null) return Collections.emptyList();
|
|
||||||
List<T> values = new ArrayList<>();
|
|
||||||
for (Object o : list) {
|
|
||||||
try {
|
|
||||||
T parsed = parser.parse(o, null);
|
|
||||||
if (parsed != null) values.add(parsed);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.source;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public interface ConfigurationWrapper<S> extends ConfigurationReader {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default ConfigurationWrapper<S> getWrapper() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull S getSource();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
Set<String> getKeys(boolean deep);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
Map<String, Object> getValues(boolean deep);
|
|
||||||
|
|
||||||
void set(@NotNull String path, @Nullable Object value);
|
|
||||||
|
|
||||||
boolean contains(@NotNull String path);
|
|
||||||
|
|
||||||
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
|
||||||
return typeClass.isInstance(get(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable Object get(@NotNull String path);
|
|
||||||
|
|
||||||
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> clazz) {
|
|
||||||
return get(path, null, clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
default @Nullable <T> T get(@NotNull String path, @NotNull ConfigValueParser<Object, T> parser) {
|
|
||||||
return get(path, null, parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_,!null,_->!null")
|
|
||||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
|
|
||||||
return get(path, defaultValue, ConfigValueParser.castObject(clazz));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("_,!null,_->!null")
|
|
||||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
|
||||||
@NotNull ConfigValueParser<Object, T> parser) {
|
|
||||||
Object value = get(path);
|
|
||||||
if (value != null) {
|
|
||||||
try {
|
|
||||||
return parser.parse(value, defaultValue);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isList(@NotNull String path);
|
|
||||||
|
|
||||||
@Nullable List<?> getList(@NotNull String path);
|
|
||||||
|
|
||||||
boolean isConfigurationSection(@NotNull String path);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
ConfigurationWrapper<S> getConfigurationSection(@NotNull String path);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.source.impl;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public abstract class FileConfigProvider<W extends ConfigurationWrapper<?>> extends ConfigurationProvider<W> {
|
|
||||||
|
|
||||||
protected final @NotNull File file;
|
|
||||||
|
|
||||||
protected FileConfigProvider(@NotNull File file) {
|
|
||||||
this.file = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull File getFile() {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initializeFile(@Nullable String sourcePath) throws IOException {
|
|
||||||
if (this.file.exists()) return;
|
|
||||||
|
|
||||||
File parent = this.file.getParentFile();
|
|
||||||
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
|
||||||
throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.file.createNewFile()) {
|
|
||||||
throw new IOException("Failed to create file " + file.getAbsolutePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sourcePath != null) {
|
|
||||||
try {
|
|
||||||
saveResource(sourcePath, true);
|
|
||||||
} catch (IllegalArgumentException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveResource(@NotNull String resourcePath, boolean replace)
|
|
||||||
throws IOException, IllegalArgumentException {
|
|
||||||
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null");
|
|
||||||
if (resourcePath.isEmpty()) throw new IllegalArgumentException("ResourcePath cannot be empty");
|
|
||||||
|
|
||||||
resourcePath = resourcePath.replace('\\', '/');
|
|
||||||
|
|
||||||
URL url = this.getClass().getClassLoader().getResource(resourcePath);
|
|
||||||
if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
|
|
||||||
|
|
||||||
File outDir = file.getParentFile();
|
|
||||||
|
|
||||||
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
|
|
||||||
if (!file.exists() || replace) {
|
|
||||||
try (OutputStream out = Files.newOutputStream(file.toPath())) {
|
|
||||||
URLConnection connection = url.openConnection();
|
|
||||||
connection.setUseCaches(false);
|
|
||||||
try (InputStream in = connection.getInputStream()) {
|
|
||||||
byte[] buf = new byte[1024];
|
|
||||||
int len;
|
|
||||||
while ((len = in.read(buf)) > 0) {
|
|
||||||
out.write(buf, 0, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public InputStream getResource(@NotNull String filename) {
|
|
||||||
try {
|
|
||||||
URL url = this.getClass().getClassLoader().getResource(filename);
|
|
||||||
if (url == null) return null;
|
|
||||||
URLConnection connection = url.openConnection();
|
|
||||||
connection.setUseCaches(false);
|
|
||||||
return connection.getInputStream();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.util;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
public class MapFactory<S extends Map<K, V>, K, V> {
|
|
||||||
|
|
||||||
private final S map;
|
|
||||||
|
|
||||||
protected MapFactory(S map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapFactory<S, K, V> put(K key, V value) {
|
|
||||||
this.map.put(key, value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapFactory<S, K, V> remove(K key) {
|
|
||||||
this.map.remove(key);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapFactory<S, K, V> clear() {
|
|
||||||
this.map.clear();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public S build() {
|
|
||||||
return get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public S get() {
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap() {
|
|
||||||
return new MapFactory<>(new HashMap<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap(K firstKey, V firstValue) {
|
|
||||||
return MapFactory.<K, V>hashMap().put(firstKey, firstValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap() {
|
|
||||||
return of(new LinkedHashMap<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap(K firstKey, V firstValue) {
|
|
||||||
return MapFactory.<K, V>linkedMap().put(firstKey, firstValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap() {
|
|
||||||
return of(new TreeMap<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap(K firstKey, V firstValue) {
|
|
||||||
return MapFactory.<K, V>treeMap().put(firstKey, firstValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <M extends Map<K, V>, K, V> MapFactory<M, K, V> of(M map) {
|
|
||||||
return new MapFactory<>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public abstract class ConfigValue<T> extends ValueManifest<T> {
|
|
||||||
|
|
||||||
public static @NotNull ConfigBuilder builder() {
|
|
||||||
return new ConfigBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ConfigValue(@NotNull ValueManifest<T> manifest) {
|
|
||||||
super(manifest.provider, manifest.configPath, manifest.headerComments, manifest.inlineComment, manifest.defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param provider Provider of config files {@link ConfigurationProvider}
|
|
||||||
* @param configPath Config path of this value
|
|
||||||
* @param headerComments Header comment contents
|
|
||||||
* @param inlineComments Inline comment contents
|
|
||||||
* @param defaultValue The default value
|
|
||||||
* @deprecated Please use {@link #ConfigValue(ValueManifest)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
protected ConfigValue(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
|
||||||
@Nullable List<String> headerComments, @Nullable String inlineComments,
|
|
||||||
@Nullable T defaultValue) {
|
|
||||||
super(provider, configPath, headerComments, inlineComments, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(@NotNull ConfigurationProvider<?> provider, boolean saveDefault, @NotNull String configPath,
|
|
||||||
@Nullable List<String> headerComments, @Nullable String inlineComments) {
|
|
||||||
this.initialize(provider, configPath, headerComments, inlineComments);
|
|
||||||
if (saveDefault) setDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到该配置的设定值(即读取到的值)。
|
|
||||||
* <br> 若初始化时未写入默认值,则可以通过 {@link #getOrDefault()} 方法在该设定值为空时获取默认值。
|
|
||||||
*
|
|
||||||
* @return 设定值
|
|
||||||
*/
|
|
||||||
public abstract @Nullable T get();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到该配置的设定值,若不存在,则返回默认值。
|
|
||||||
*
|
|
||||||
* @return 设定值或默认值
|
|
||||||
*/
|
|
||||||
public @Nullable T getOrDefault() {
|
|
||||||
return getOptional().orElse(getDefaultValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到该配置的非空值。
|
|
||||||
*
|
|
||||||
* @return 非空值
|
|
||||||
* @throws NullPointerException 对应数据为空时抛出
|
|
||||||
*/
|
|
||||||
public @NotNull T getNotNull() {
|
|
||||||
return Objects.requireNonNull(getOrDefault(), "Value(" + configPath + ") is null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Optional<@Nullable T> getOptional() {
|
|
||||||
return Optional.ofNullable(get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设定该配置的值。
|
|
||||||
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。
|
|
||||||
*
|
|
||||||
* @param value 配置的值
|
|
||||||
*/
|
|
||||||
public abstract void set(@Nullable T value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化该配置的默认值。
|
|
||||||
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。
|
|
||||||
*/
|
|
||||||
public void setDefault() {
|
|
||||||
setDefault(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将该配置的值设置为默认值。
|
|
||||||
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。
|
|
||||||
*
|
|
||||||
* @param override 是否覆盖已设定的值
|
|
||||||
*/
|
|
||||||
public void setDefault(boolean override) {
|
|
||||||
if (!override && getConfiguration().contains(getConfigPath())) return;
|
|
||||||
Optional.ofNullable(getDefaultValue()).ifPresent(this::set);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断加载的配置是否与默认值相同。
|
|
||||||
*
|
|
||||||
* @return 获取当前值是否为默认值。
|
|
||||||
*/
|
|
||||||
public boolean isDefault() {
|
|
||||||
return Objects.equals(getDefaultValue(), get());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ConfigValue Manifests.
|
|
||||||
* The basic information that describes a configuration value.
|
|
||||||
*
|
|
||||||
* @param <T> Value type
|
|
||||||
* @author CarmJos
|
|
||||||
*/
|
|
||||||
public class ValueManifest<T> {
|
|
||||||
|
|
||||||
public static <V> ValueManifest<V> of(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
|
||||||
@Nullable List<String> headerComments, @Nullable String inlineComments) {
|
|
||||||
return new ValueManifest<>(provider, configPath, headerComments, inlineComments, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> ValueManifest<V> of(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
|
||||||
@Nullable List<String> headerComments, @Nullable String inlineComments,
|
|
||||||
@Nullable V defaultValue) {
|
|
||||||
return new ValueManifest<>(provider, configPath, headerComments, inlineComments, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @Nullable ConfigurationProvider<?> provider;
|
|
||||||
protected @Nullable String configPath;
|
|
||||||
|
|
||||||
protected @Nullable List<String> headerComments;
|
|
||||||
protected @Nullable String inlineComment;
|
|
||||||
|
|
||||||
protected @Nullable T defaultValue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param provider Provider of config files {@link ConfigurationProvider}
|
|
||||||
* @param configPath Config path of this value
|
|
||||||
* @param headerComments Header comment contents
|
|
||||||
* @param inlineComment Inline comment content
|
|
||||||
* @param defaultValue The default value
|
|
||||||
*/
|
|
||||||
public ValueManifest(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
|
||||||
@Nullable List<String> headerComments, @Nullable String inlineComment,
|
|
||||||
@Nullable T defaultValue) {
|
|
||||||
this.provider = provider;
|
|
||||||
this.configPath = configPath;
|
|
||||||
this.headerComments = headerComments;
|
|
||||||
this.inlineComment = inlineComment;
|
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The initialize method for {@link ConfigInitializer}, which is used to initialize the value.
|
|
||||||
*
|
|
||||||
* @param provider Provider of config files {@link ConfigurationProvider}
|
|
||||||
* @param configPath Config path of this value
|
|
||||||
* @param headerComments Header comment contents
|
|
||||||
* @param inlineComment Inline comment content
|
|
||||||
*/
|
|
||||||
protected void initialize(@NotNull ConfigurationProvider<?> provider, @NotNull String configPath,
|
|
||||||
@Nullable List<String> headerComments, @Nullable String inlineComment) {
|
|
||||||
if (this.provider == null) this.provider = provider;
|
|
||||||
if (this.configPath == null) this.configPath = configPath;
|
|
||||||
if (this.headerComments == null) this.headerComments = headerComments;
|
|
||||||
if (this.inlineComment == null) this.inlineComment = inlineComment;
|
|
||||||
|
|
||||||
if (getHeaderComments() != null) {
|
|
||||||
this.provider.setHeaderComment(getConfigPath(), getHeaderComments());
|
|
||||||
}
|
|
||||||
if (getInlineComment() != null) {
|
|
||||||
this.provider.setInlineComment(getConfigPath(), getInlineComment());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable T getDefaultValue() {
|
|
||||||
return this.defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultValue(@Nullable T defaultValue) {
|
|
||||||
this.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigurationProvider<?> getProvider() {
|
|
||||||
return Optional.ofNullable(this.provider)
|
|
||||||
.orElseThrow(() -> new IllegalStateException("Value(" + configPath + ") does not have a provider."));
|
|
||||||
}
|
|
||||||
|
|
||||||
public final @NotNull ConfigurationWrapper<?> getConfiguration() {
|
|
||||||
try {
|
|
||||||
return getProvider().getConfiguration();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
throw new IllegalStateException("Value(" + configPath + ") has not been initialized", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull String getConfigPath() {
|
|
||||||
return Optional.ofNullable(this.configPath)
|
|
||||||
.orElseThrow(() -> new IllegalStateException("No section path provided."));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Object getValue() {
|
|
||||||
String path = getConfigPath(); // 当未指定路径时,优先抛出异常
|
|
||||||
return getConfiguration().get(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setValue(@Nullable Object value) {
|
|
||||||
getConfiguration().set(getConfigPath(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String getInlineComment() {
|
|
||||||
return inlineComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
public @Nullable List<String> getHeaderComments() {
|
|
||||||
return headerComments;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.impl;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
|
|
||||||
|
|
||||||
|
|
||||||
protected @Nullable T cachedValue;
|
|
||||||
protected long parsedTime = -1;
|
|
||||||
|
|
||||||
protected CachedConfigValue(@NotNull ValueManifest<T> manifest) {
|
|
||||||
super(manifest);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected T updateCache(T value) {
|
|
||||||
this.parsedTime = System.currentTimeMillis();
|
|
||||||
this.cachedValue = value;
|
|
||||||
return getCachedValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable T getCachedValue() {
|
|
||||||
return cachedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExpired() {
|
|
||||||
return this.parsedTime <= 0 || getProvider().isExpired(this.parsedTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final T getDefaultFirst(@Nullable T value) {
|
|
||||||
return updateCache(this.defaultValue == null ? value : this.defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected @Nullable T getCachedOrDefault() {
|
|
||||||
return getCachedOrDefault(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract("!null->!null")
|
|
||||||
protected T getCachedOrDefault(@Nullable T emptyValue) {
|
|
||||||
if (getCachedValue() != null) return getCachedValue();
|
|
||||||
else if (getDefaultValue() != null) return getDefaultValue();
|
|
||||||
else return emptyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.impl;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapCreator;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public abstract class ConfigValueMap<K, V, S> extends CachedConfigValue<Map<K, V>> implements Map<K, V> {
|
|
||||||
|
|
||||||
public static <K, V> @NotNull ConfigMapCreator<K, V> builderOf(@NotNull Class<K> keyClass,
|
|
||||||
@NotNull Class<V> valueClass) {
|
|
||||||
return builder().asMap(keyClass, valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final @NotNull Supplier<? extends Map<K, V>> supplier;
|
|
||||||
|
|
||||||
protected final @NotNull Class<? super S> sourceClass;
|
|
||||||
protected final @NotNull Class<K> keyClass;
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
protected final @NotNull ConfigDataFunction<String, K> keyParser;
|
|
||||||
protected final @NotNull ConfigDataFunction<S, V> valueParser;
|
|
||||||
|
|
||||||
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
|
|
||||||
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
|
|
||||||
|
|
||||||
|
|
||||||
protected ConfigValueMap(@NotNull ValueManifest<Map<K, V>> manifest, @NotNull Class<? super S> sourceClass,
|
|
||||||
@NotNull Supplier<? extends Map<K, V>> mapObjSupplier,
|
|
||||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, Object> valueSerializer) {
|
|
||||||
super(manifest);
|
|
||||||
this.supplier = mapObjSupplier;
|
|
||||||
this.sourceClass = sourceClass;
|
|
||||||
this.keyClass = keyClass;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.keyParser = keyParser;
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
this.keySerializer = keySerializer;
|
|
||||||
this.valueSerializer = valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Class<? super S> getSourceClass() {
|
|
||||||
return sourceClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Class<K> getKeyClass() {
|
|
||||||
return keyClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Class<V> getValueClass() {
|
|
||||||
return valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigDataFunction<String, K> getKeyParser() {
|
|
||||||
return keyParser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigDataFunction<S, V> getValueParser() {
|
|
||||||
return valueParser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigDataFunction<K, String> getKeySerializer() {
|
|
||||||
return keySerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigDataFunction<V, Object> getValueSerializer() {
|
|
||||||
return valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract S getSource(ConfigurationWrapper<?> section, String dataKey);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Map<K, V> get() {
|
|
||||||
if (!isExpired()) return getCachedOrDefault(supplier.get());
|
|
||||||
|
|
||||||
// 已过时的数据,需要重新解析一次。
|
|
||||||
Map<K, V> map = supplier.get();
|
|
||||||
|
|
||||||
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
|
|
||||||
if (section == null) return getDefaultFirst(map);
|
|
||||||
|
|
||||||
Set<String> keys = section.getKeys(false);
|
|
||||||
if (keys.isEmpty()) return getDefaultFirst(map);
|
|
||||||
|
|
||||||
for (String dataKey : keys) {
|
|
||||||
S dataVal = getSource(section, dataKey);
|
|
||||||
if (dataVal == null) continue;
|
|
||||||
try {
|
|
||||||
K key = keyParser.parse(dataKey);
|
|
||||||
V value = valueParser.parse(dataVal);
|
|
||||||
map.put(key, value);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateCache(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(Object key) {
|
|
||||||
return get().get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public V getNotNull(Object key) {
|
|
||||||
return Objects.requireNonNull(get(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(@Nullable Map<K, V> value) {
|
|
||||||
updateCache(value);
|
|
||||||
if (value == null) setValue(null);
|
|
||||||
else {
|
|
||||||
Map<String, Object> data = new LinkedHashMap<>();
|
|
||||||
for (Map.Entry<K, V> entry : value.entrySet()) {
|
|
||||||
try {
|
|
||||||
data.put(
|
|
||||||
keySerializer.parse(entry.getKey()),
|
|
||||||
valueSerializer.parse(entry.getValue())
|
|
||||||
);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setValue(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> @NotNull T modifyValue(Function<Map<K, V>, T> function) {
|
|
||||||
Map<K, V> m = get();
|
|
||||||
T result = function.apply(m);
|
|
||||||
set(m);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Map<K, V> modifyMap(Consumer<Map<K, V>> consumer) {
|
|
||||||
Map<K, V> m = get();
|
|
||||||
consumer.accept(m);
|
|
||||||
set(m);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return get().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return get().isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsKey(Object key) {
|
|
||||||
return get().containsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsValue(Object value) {
|
|
||||||
return get().containsValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public V put(K key, V value) {
|
|
||||||
return modifyValue(m -> m.put(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V remove(Object key) {
|
|
||||||
return modifyValue(m -> m.remove(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
|
|
||||||
modifyMap(map -> map.putAll(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
modifyMap(Map::clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<K> keySet() {
|
|
||||||
return get().keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Collection<V> values() {
|
|
||||||
return get().values();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
@Unmodifiable
|
|
||||||
public Set<Entry<K, V>> entrySet() {
|
|
||||||
return get().entrySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.type;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements List<V> {
|
|
||||||
|
|
||||||
public static <V> @NotNull ConfigListBuilder<V> builderOf(@NotNull Class<V> valueClass) {
|
|
||||||
return builder().asList(valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> @NotNull ConfiguredList<V> of(@NotNull Class<V> valueClass, @NotNull Collection<V> defaults) {
|
|
||||||
return builderOf(valueClass).fromObject().defaults(defaults).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SafeVarargs
|
|
||||||
public static <V> @NotNull ConfiguredList<V> of(@NotNull Class<V> valueClass, @NotNull V... defaults) {
|
|
||||||
return builderOf(valueClass).fromObject().defaults(defaults).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SafeVarargs
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <V> @NotNull ConfiguredList<V> of(@NotNull V defaultValue, @NotNull V... moreDefaults) {
|
|
||||||
Collection<V> values = new ArrayList<>();
|
|
||||||
values.add(defaultValue);
|
|
||||||
values.addAll(Arrays.asList(moreDefaults));
|
|
||||||
return of((Class<V>) defaultValue.getClass(), values);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
protected final @NotNull ConfigDataFunction<Object, V> parser;
|
|
||||||
protected final @NotNull ConfigDataFunction<V, Object> serializer;
|
|
||||||
|
|
||||||
public ConfiguredList(@NotNull ValueManifest<List<V>> manifest, @NotNull Class<V> valueClass,
|
|
||||||
@NotNull ConfigDataFunction<Object, V> parser,
|
|
||||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
|
||||||
super(manifest);
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.parser = parser;
|
|
||||||
this.serializer = serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull List<V> get() {
|
|
||||||
if (!isExpired()) return getCachedOrDefault(new ArrayList<>());
|
|
||||||
// Data that is outdated and needs to be parsed again.
|
|
||||||
List<V> list = new ArrayList<>();
|
|
||||||
List<?> data = getConfiguration().contains(getConfigPath()) ?
|
|
||||||
getConfiguration().getList(getConfigPath()) : null;
|
|
||||||
if (data == null) return getDefaultFirst(list);
|
|
||||||
for (Object dataVal : data) {
|
|
||||||
if (dataVal == null) continue;
|
|
||||||
try {
|
|
||||||
list.add(parser.parse(dataVal));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return updateCache(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get(int index) {
|
|
||||||
return get().get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull List<V> copy() {
|
|
||||||
return new ArrayList<>(get());
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> @NotNull T handle(Function<List<V>, T> function) {
|
|
||||||
List<V> list = get();
|
|
||||||
T result = function.apply(list);
|
|
||||||
set(list);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull List<V> modify(Consumer<List<V>> consumer) {
|
|
||||||
List<V> list = get();
|
|
||||||
consumer.accept(list);
|
|
||||||
set(list);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(@Nullable List<V> value) {
|
|
||||||
updateCache(value);
|
|
||||||
if (value == null) setValue(null);
|
|
||||||
else {
|
|
||||||
List<Object> data = new ArrayList<>();
|
|
||||||
for (V val : value) {
|
|
||||||
if (val == null) continue;
|
|
||||||
try {
|
|
||||||
data.add(serializer.parse(val));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setValue(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V set(int index, V element) {
|
|
||||||
return handle(list -> list.set(index, element));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return get().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return get().isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(Object o) {
|
|
||||||
return get().contains(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Iterator<V> iterator() {
|
|
||||||
return get().iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Object @NotNull [] toArray() {
|
|
||||||
return get().toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public <T> T @NotNull [] toArray(@NotNull T[] a) {
|
|
||||||
return get().toArray(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsAll(@NotNull Collection<?> c) {
|
|
||||||
return new HashSet<>(get()).containsAll(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean add(V v) {
|
|
||||||
handle(list -> list.add(v));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(int index, V element) {
|
|
||||||
modify(list -> list.add(index, element));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addAll(@NotNull Collection<? extends V> c) {
|
|
||||||
return handle(list -> list.addAll(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean addAll(int index, @NotNull Collection<? extends V> c) {
|
|
||||||
return handle(list -> list.addAll(index, c));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean remove(Object o) {
|
|
||||||
return handle(list -> list.remove(o));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V remove(int index) {
|
|
||||||
return handle(list -> list.remove(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean removeAll(@NotNull Collection<?> c) {
|
|
||||||
return handle(list -> list.removeAll(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean retainAll(@NotNull Collection<?> c) {
|
|
||||||
return handle(list -> list.retainAll(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
modify(List::clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int indexOf(Object o) {
|
|
||||||
return get().indexOf(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int lastIndexOf(Object o) {
|
|
||||||
return get().lastIndexOf(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public ListIterator<V> listIterator() {
|
|
||||||
return get().listIterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public ListIterator<V> listIterator(int index) {
|
|
||||||
return get().listIterator(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public List<V> subList(int fromIndex, int toIndex) {
|
|
||||||
return get().subList(fromIndex, toIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.type;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.impl.ConfigValueMap;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ConfiguredMap<K, V> extends ConfigValueMap<K, V, Object> {
|
|
||||||
|
|
||||||
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest,
|
|
||||||
@NotNull Supplier<? extends Map<K, V>> mapObjSupplier,
|
|
||||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, Object> valueSerializer) {
|
|
||||||
super(manifest, Object.class, mapObjSupplier, keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getSource(ConfigurationWrapper<?> section, String dataKey) {
|
|
||||||
return section.get(dataKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.type;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.value.SectionValueBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class ConfiguredSection<V> extends CachedConfigValue<V> {
|
|
||||||
|
|
||||||
public static <V> @NotNull SectionValueBuilder<V> builderOf(@NotNull Class<V> valueClass) {
|
|
||||||
return builder().asValue(valueClass).fromSection();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
protected final @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser;
|
|
||||||
protected final @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
|
|
||||||
|
|
||||||
public ConfiguredSection(@NotNull ValueManifest<V> manifest, @NotNull Class<V> valueClass,
|
|
||||||
@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser,
|
|
||||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
|
||||||
super(manifest);
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.parser = parser;
|
|
||||||
this.serializer = serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's type class
|
|
||||||
*/
|
|
||||||
public @NotNull Class<V> getValueClass() {
|
|
||||||
return valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's parser, cast value from section.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> getParser() {
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's serializer, serialize value to section.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> getSerializer() {
|
|
||||||
return serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Get the value that parsed from the configuration section.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable V get() {
|
|
||||||
if (!isExpired()) return getCachedOrDefault();
|
|
||||||
// Data that is outdated and needs to be parsed again.
|
|
||||||
|
|
||||||
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
|
|
||||||
if (section == null) return getDefaultValue();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// If there are no errors, update the cache and return.
|
|
||||||
return updateCache(this.parser.parse(section, this.defaultValue));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// There was a parsing error, prompted and returned the default value.
|
|
||||||
e.printStackTrace();
|
|
||||||
return getDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use the specified value to update the configuration section.
|
|
||||||
* Will use {@link #getSerializer()} to serialize the value to section.
|
|
||||||
*
|
|
||||||
* @param value The value that needs to be set in the configuration.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void set(V value) {
|
|
||||||
updateCache(value);
|
|
||||||
if (value == null) setValue(null);
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
setValue(serializer.parse(value));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.type;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.impl.ConfigValueMap;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ConfiguredSectionMap<K, V> extends ConfigValueMap<K, V, ConfigurationWrapper<?>> {
|
|
||||||
|
|
||||||
public ConfiguredSectionMap(@NotNull ValueManifest<Map<K, V>> manifest,
|
|
||||||
@NotNull Supplier<? extends Map<K, V>> mapObjSupplier,
|
|
||||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
|
|
||||||
super(
|
|
||||||
manifest, ConfigurationWrapper.class, mapObjSupplier,
|
|
||||||
keyClass, keyParser, valueClass, valueParser,
|
|
||||||
keySerializer, valueSerializer.andThen(s -> (Object) s)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigurationWrapper<?> getSource(ConfigurationWrapper<?> section, String dataKey) {
|
|
||||||
return section.getConfigurationSection(dataKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.type;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
|
||||||
|
|
||||||
public static <V> ConfigValueBuilder<V> builderOf(Class<V> valueClass) {
|
|
||||||
return builder().asValue(valueClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <V> ConfiguredValue<V> of(@NotNull V defaultValue) {
|
|
||||||
return of((Class<V>) defaultValue.getClass(), defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> ConfiguredValue<V> of(Class<V> valueClass) {
|
|
||||||
return of(valueClass, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <V> ConfiguredValue<V> of(Class<V> valueClass, @Nullable V defaultValue) {
|
|
||||||
return builderOf(valueClass).fromObject().defaults(defaultValue).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
protected final @NotNull ConfigValueParser<Object, V> parser;
|
|
||||||
protected final @NotNull ConfigDataFunction<V, Object> serializer;
|
|
||||||
|
|
||||||
public ConfiguredValue(@NotNull ValueManifest<V> manifest, @NotNull Class<V> valueClass,
|
|
||||||
@NotNull ConfigValueParser<Object, V> parser,
|
|
||||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
|
||||||
super(manifest);
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.parser = parser;
|
|
||||||
this.serializer = serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's type class
|
|
||||||
*/
|
|
||||||
public @NotNull Class<V> getValueClass() {
|
|
||||||
return valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's parser, cast value from base object.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigValueParser<Object, V> getParser() {
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's serializer, serialize value to base object.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigDataFunction<V, Object> getSerializer() {
|
|
||||||
return serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get() {
|
|
||||||
if (!isExpired()) return getCachedOrDefault();
|
|
||||||
// Data that is outdated and needs to be parsed again.
|
|
||||||
|
|
||||||
Object value = getValue();
|
|
||||||
if (value == null) return getDefaultValue(); // 获取的值不存在,直接使用默认值。
|
|
||||||
try {
|
|
||||||
// If there are no errors, update the cache and return.
|
|
||||||
return updateCache(this.parser.parse(value, this.defaultValue));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// There was a parsing error, prompted and returned the default value.
|
|
||||||
e.printStackTrace();
|
|
||||||
return getDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of the configuration path.
|
|
||||||
* Will use {@link #getSerializer()} to serialize the value.
|
|
||||||
*
|
|
||||||
* @param value The value to be set
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void set(V value) {
|
|
||||||
updateCache(value);
|
|
||||||
if (value == null) setValue(null);
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
setValue(serializer.parse(value));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
+20
-20
@@ -1,4 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.core.function;
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@@ -7,39 +7,39 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ConfigDataFunction<T, R> {
|
public interface DataFunction<T, R> {
|
||||||
|
|
||||||
@NotNull R parse(@NotNull T data) throws Exception;
|
@NotNull R handle(@NotNull T data) throws Exception;
|
||||||
|
|
||||||
default <V> @NotNull ConfigDataFunction<T, V> andThen(@NotNull ConfigDataFunction<? super R, V> after) {
|
default <V> @NotNull DataFunction<T, V> andThen(@NotNull DataFunction<? super R, V> after) {
|
||||||
Objects.requireNonNull(after);
|
Objects.requireNonNull(after);
|
||||||
return data -> after.parse(parse(data));
|
return data -> after.handle(handle(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, T> identity() {
|
static <T> @NotNull DataFunction<T, T> identity() {
|
||||||
return input -> input;
|
return input -> input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, T> identity(Class<T> type) {
|
static <T> @NotNull DataFunction<T, T> identity(Class<T> type) {
|
||||||
return input -> input;
|
return input -> input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T, V> @NotNull ConfigDataFunction<T, V> required() {
|
static <T, V> @NotNull DataFunction<T, V> required() {
|
||||||
return input -> {
|
return input -> {
|
||||||
throw new IllegalArgumentException("Please specify the value parser.");
|
throw new IllegalArgumentException("Please specify the value parser.");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, Object> toObject() {
|
static <T> @NotNull DataFunction<T, Object> toObject() {
|
||||||
return input -> input;
|
return input -> input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <V> @NotNull ConfigDataFunction<Object, V> castObject(Class<V> valueClass) {
|
static <V> @NotNull DataFunction<Object, V> castObject(Class<V> valueClass) {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
||||||
@@ -47,7 +47,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <V> @NotNull ConfigDataFunction<String, V> castFromString(Class<V> valueClass) {
|
static <V> @NotNull DataFunction<String, V> castFromString(Class<V> valueClass) {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||||
@@ -55,7 +55,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, String> castToString() {
|
static <T> @NotNull DataFunction<T, String> castToString() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof String) return (String) input;
|
if (input instanceof String) return (String) input;
|
||||||
else if (input instanceof Enum<?>) return ((Enum<?>) input).name();
|
else if (input instanceof Enum<?>) return ((Enum<?>) input).name();
|
||||||
@@ -64,7 +64,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <V> @NotNull ConfigDataFunction<String, V> parseString(Class<V> valueClass) {
|
static <V> @NotNull DataFunction<String, V> parseString(Class<V> valueClass) {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||||
@@ -72,7 +72,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Integer> intValue() {
|
static @NotNull DataFunction<Object, Integer> intValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Integer) {
|
if (input instanceof Integer) {
|
||||||
return (Integer) input;
|
return (Integer) input;
|
||||||
@@ -83,7 +83,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Short> shortValue() {
|
static @NotNull DataFunction<Object, Short> shortValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Short) {
|
if (input instanceof Short) {
|
||||||
return (Short) input;
|
return (Short) input;
|
||||||
@@ -94,7 +94,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Double> doubleValue() {
|
static @NotNull DataFunction<Object, Double> doubleValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Double) {
|
if (input instanceof Double) {
|
||||||
return (Double) input;
|
return (Double) input;
|
||||||
@@ -105,7 +105,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Byte> byteValue() {
|
static @NotNull DataFunction<Object, Byte> byteValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Byte) {
|
if (input instanceof Byte) {
|
||||||
return (Byte) input;
|
return (Byte) input;
|
||||||
@@ -116,7 +116,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Float> floatValue() {
|
static @NotNull DataFunction<Object, Float> floatValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Float) {
|
if (input instanceof Float) {
|
||||||
return (Float) input;
|
return (Float) input;
|
||||||
@@ -127,7 +127,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Long> longValue() {
|
static @NotNull DataFunction<Object, Long> longValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Long) {
|
if (input instanceof Long) {
|
||||||
return (Long) input;
|
return (Long) input;
|
||||||
@@ -138,7 +138,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Boolean> booleanValue() {
|
static @NotNull DataFunction<Object, Boolean> booleanValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Boolean) {
|
if (input instanceof Boolean) {
|
||||||
return (Boolean) input;
|
return (Boolean) input;
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueHandler<T, R> {
|
||||||
|
|
||||||
|
@Nullable R handle(@NotNull ConfigurationHolder<?> holder, @NotNull T data) throws Exception;
|
||||||
|
|
||||||
|
default <V> ValueHandler<T, V> andThen(@NotNull ValueHandler<R, V> after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
return ((provider, data) -> {
|
||||||
|
R result = handle(provider, data);
|
||||||
|
if (result == null) return null;
|
||||||
|
else return after.handle(provider, result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
default <V> ValueHandler<V, R> compose(@NotNull ValueHandler<? super V, ? extends T> before) {
|
||||||
|
Objects.requireNonNull(before);
|
||||||
|
return ((provider, data) -> {
|
||||||
|
T result = before.handle(provider, data);
|
||||||
|
if (result == null) return null;
|
||||||
|
return handle(provider, result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
default <V> ValueHandler<V, R> compose(@NotNull DataFunction<? super V, ? extends T> before) {
|
||||||
|
Objects.requireNonNull(before);
|
||||||
|
return ((provider, data) -> {
|
||||||
|
T result = before.handle(data);
|
||||||
|
return handle(provider, result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
static <T> @NotNull ValueHandler<T, T> identity() {
|
||||||
|
return (provider, input) -> input;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
static <T> @NotNull ValueHandler<T, Object> toObject() {
|
||||||
|
return ConfigurationHolder::serialize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
static <T> @NotNull ValueHandler<T, String> stringValue() {
|
||||||
|
return (provider, input) -> String.valueOf(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
static <T> @NotNull ValueHandler<Object, T> fromObject(ValueType<T> type) {
|
||||||
|
return (provider, input) -> provider.deserialize(type, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
static <T, V> @NotNull ValueHandler<T, V> required() {
|
||||||
|
return (provider, input) -> {
|
||||||
|
throw new IllegalArgumentException("Please specify the value parser.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package cc.carm.lib.configuration.source;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.*;
|
||||||
|
import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.strandard.StandardAdapters;
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
|
import cc.carm.lib.configuration.source.loader.PathGenerator;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public abstract class ConfigurationFactory<
|
||||||
|
SOURCE extends ConfigureSource<?, ?, SOURCE>,
|
||||||
|
HOLDER extends ConfigurationHolder<SOURCE>,
|
||||||
|
SELF
|
||||||
|
> {
|
||||||
|
|
||||||
|
protected ValueAdapterRegistry adapters = new ValueAdapterRegistry();
|
||||||
|
protected ConfigurationOptionHolder options = new ConfigurationOptionHolder();
|
||||||
|
protected ConfigurationInitializer initializer = new ConfigurationInitializer();
|
||||||
|
|
||||||
|
public ConfigurationFactory() {
|
||||||
|
this.adapters.register(PrimitiveAdapter.ADAPTERS);
|
||||||
|
this.adapters.register(StandardAdapters.SECTION_ADAPTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract SELF self();
|
||||||
|
|
||||||
|
|
||||||
|
public SELF adapters(ValueAdapterRegistry adapters) {
|
||||||
|
this.adapters = adapters;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF adapter(Consumer<ValueAdapterRegistry> adapterRegistryConsumer) {
|
||||||
|
adapterRegistryConsumer.accept(adapters);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SELF adapter(@NotNull ValueAdapter<T> adapter) {
|
||||||
|
return adapter(a -> a.register(adapter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||||
|
return adapter(a -> a.register(type, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueParser<T> parser) {
|
||||||
|
return adapter(a -> a.register(type, parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <FROM, TO> SELF adapter(@NotNull Class<FROM> from, @NotNull Class<TO> to,
|
||||||
|
@NotNull DataFunction<FROM, TO> parser,
|
||||||
|
@NotNull DataFunction<TO, FROM> serializer) {
|
||||||
|
return adapter(a -> a.register(from, to, parser, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <FROM, TO> SELF adapter(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
|
||||||
|
@NotNull DataFunction<FROM, TO> parser,
|
||||||
|
@NotNull DataFunction<TO, FROM> serializer) {
|
||||||
|
return adapter(a -> a.register(from, to, parser, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||||
|
return adapter(a -> a.register(type, serializer, parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SELF adapter(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||||
|
return adapter(ValueType.of(type), serializer, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF options(ConfigurationOptionHolder options) {
|
||||||
|
this.options = options;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF option(Consumer<ConfigurationOptionHolder> optionsConsumer) {
|
||||||
|
optionsConsumer.accept(options);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <O> SELF option(ConfigurationOption<O> option, O value) {
|
||||||
|
return option(o -> o.set(option, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SELF initializer(ConfigurationInitializer initializer) {
|
||||||
|
this.initializer = initializer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF initializer(Consumer<ConfigurationInitializer> initializerConsumer) {
|
||||||
|
initializerConsumer.accept(initializer);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF pathGenerator(PathGenerator generator) {
|
||||||
|
return initializer(loader -> {
|
||||||
|
loader.pathGenerator(generator);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public <M, A extends Annotation> SELF metaAnnotation(@NotNull Class<A> annotation,
|
||||||
|
@NotNull ConfigurationMetadata<M> metadata,
|
||||||
|
@NotNull Function<A, M> extractor) {
|
||||||
|
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract @NotNull HOLDER build();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package cc.carm.lib.configuration.source;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, SOURCE>> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueAdapterRegistry adapters;
|
||||||
|
protected final @NotNull ConfigurationOptionHolder options;
|
||||||
|
protected final @NotNull Map<String, ConfigurationMetaHolder> metadata;
|
||||||
|
|
||||||
|
protected final @NotNull ConfigurationInitializer initializer;
|
||||||
|
|
||||||
|
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
|
||||||
|
@NotNull ConfigurationOptionHolder options,
|
||||||
|
@NotNull Map<String, ConfigurationMetaHolder> metadata,
|
||||||
|
@NotNull ConfigurationInitializer initializer) {
|
||||||
|
this.initializer = initializer;
|
||||||
|
this.adapters = adapters;
|
||||||
|
this.options = options;
|
||||||
|
this.metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract @NotNull SOURCE config();
|
||||||
|
|
||||||
|
public void reload() throws Exception {
|
||||||
|
config().reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save() throws Exception {
|
||||||
|
config().save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationOptionHolder options() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Map<String, ConfigurationMetaHolder> metadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigurationMetaHolder metadata(@Nullable String path) {
|
||||||
|
return metadata().computeIfAbsent(path, k -> new ConfigurationMetaHolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ValueAdapterRegistry adapters() {
|
||||||
|
return this.adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationInitializer initializer() {
|
||||||
|
return initializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_,null -> null")
|
||||||
|
public <T> T deserialize(@NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||||
|
return adapters().deserialize(this, type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_,null -> null")
|
||||||
|
public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||||
|
return adapters().deserialize(this, type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("null -> null")
|
||||||
|
public <T> Object serialize(@Nullable T value) throws Exception {
|
||||||
|
return adapters().serialize(this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(Class<? extends Configuration> configClass) {
|
||||||
|
try {
|
||||||
|
initializer.initialize(this, configClass);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(@NotNull Configuration config) {
|
||||||
|
try {
|
||||||
|
initializer.initialize(this, config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(@NotNull ValueManifest<?> value) {
|
||||||
|
value.holder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package cc.carm.lib.configuration.source.loader;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ConfigInitializeHandler<T> {
|
||||||
|
|
||||||
|
static <T> ConfigInitializeHandler<T> start() {
|
||||||
|
return (provider, path, value) -> {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path, @NotNull T value) throws Exception;
|
||||||
|
|
||||||
|
default ConfigInitializeHandler<T> andThen(ConfigInitializeHandler<T> after) {
|
||||||
|
return (provider, path, value) -> {
|
||||||
|
whenInitialize(provider, path, value);
|
||||||
|
after.whenInitialize(provider, path, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default ConfigInitializeHandler<T> compose(ConfigInitializeHandler<T> before) {
|
||||||
|
return (provider, path, value) -> {
|
||||||
|
before.whenInitialize(provider, path, value);
|
||||||
|
whenInitialize(provider, path, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+183
@@ -0,0 +1,183 @@
|
|||||||
|
package cc.carm.lib.configuration.source.loader;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||||
|
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration initializer,
|
||||||
|
* used to initialize {@link ConfigValue}s from {@link Configuration} classes.
|
||||||
|
*/
|
||||||
|
public class ConfigurationInitializer {
|
||||||
|
|
||||||
|
protected @NotNull PathGenerator pathGenerator;
|
||||||
|
protected @NotNull ConfigInitializeHandler<Field> fieldInitializer;
|
||||||
|
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer;
|
||||||
|
|
||||||
|
public ConfigurationInitializer() {
|
||||||
|
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
|
||||||
|
@NotNull ConfigInitializeHandler<Field> fieldInitializer,
|
||||||
|
@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
||||||
|
this.pathGenerator = pathGenerator;
|
||||||
|
this.fieldInitializer = fieldInitializer;
|
||||||
|
this.classInitializer = classInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pathGenerator(@NotNull PathGenerator pathGenerator) {
|
||||||
|
this.pathGenerator = pathGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull PathGenerator pathGenerator() {
|
||||||
|
return pathGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigInitializeHandler<Field> fieldInitializer() {
|
||||||
|
return fieldInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
|
||||||
|
this.fieldInitializer = fieldInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigInitializeHandler<Class<? extends Configuration>> classInitializer() {
|
||||||
|
return classInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
||||||
|
this.classInitializer = classInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
|
||||||
|
this.fieldInitializer = this.fieldInitializer.andThen(fieldInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
||||||
|
this.classInitializer = this.classInitializer.andThen(classInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T, A extends Annotation> void registerAnnotation(@NotNull Class<A> annotation,
|
||||||
|
@NotNull ConfigurationMetadata<T> metadata,
|
||||||
|
@NotNull Function<A, T> extractor) {
|
||||||
|
appendFieldInitializer((holder, path, field) -> {
|
||||||
|
A data = field.getAnnotation(annotation);
|
||||||
|
if (data == null) return;
|
||||||
|
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||||
|
});
|
||||||
|
appendClassInitializer((holder, path, clazz) -> {
|
||||||
|
A data = clazz.getAnnotation(annotation);
|
||||||
|
if (data == null) return;
|
||||||
|
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath, @NotNull Field field) {
|
||||||
|
return pathGenerator.getFieldPath(holder, parentPath, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getClassPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath,
|
||||||
|
@NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||||
|
return pathGenerator.getClassPath(holder, parentPath, clazz, clazzField);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull Configuration config) throws Exception {
|
||||||
|
initializeInstance(holder, config, null, null);
|
||||||
|
if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull Class<? extends Configuration> clazz) throws Exception {
|
||||||
|
initializeStaticClass(holder, clazz, null, null);
|
||||||
|
if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 针对实例类的初始化方法
|
||||||
|
protected void initializeInstance(@NotNull ConfigurationHolder<?> holder, @NotNull Configuration root,
|
||||||
|
@Nullable String parentPath, @Nullable Field configField) {
|
||||||
|
String path = getClassPath(holder, parentPath, root.getClass(), configField);
|
||||||
|
try {
|
||||||
|
this.classInitializer.whenInitialize(holder, path, root.getClass());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 针对静态类的初始化方法
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void initializeStaticClass(@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull Class<?> clazz,
|
||||||
|
@Nullable String parentPath, @Nullable Field configField) {
|
||||||
|
if (!Configuration.class.isAssignableFrom(clazz)) return; // Only Configuration class can be initialized.
|
||||||
|
|
||||||
|
String path = getClassPath(holder, parentPath, clazz, configField);
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
initializeField(holder, clazz, field, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holder.options().get(StandardOptions.LOAD_SUB_CLASSES)) {
|
||||||
|
Class<?>[] classes = clazz.getDeclaredClasses();
|
||||||
|
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
||||||
|
initializeStaticClass(holder, classes[i], path, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeField(@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull Object source, @NotNull Field field, @Nullable String parent) {
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object object = field.get(source);
|
||||||
|
//
|
||||||
|
if (object instanceof ConfigValue<?>) {
|
||||||
|
// 目标是 ConfigValue 实例,进行具体的初始化注入
|
||||||
|
ConfigValue<?> value = (ConfigValue<?>) object;
|
||||||
|
String path = getFieldPath(holder, parent, field);
|
||||||
|
if (path == null) return;
|
||||||
|
value.initialize(holder, path);
|
||||||
|
try {
|
||||||
|
this.fieldInitializer.whenInitialize(holder, path, field);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (holder.options().get(StandardOptions.SET_DEFAULTS)) {
|
||||||
|
value.setDefault();
|
||||||
|
}
|
||||||
|
} else if (source instanceof Configuration && object instanceof Configuration) {
|
||||||
|
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
|
||||||
|
initializeInstance(holder, (Configuration) object, parent, field);
|
||||||
|
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
||||||
|
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
||||||
|
initializeStaticClass(holder, (Class<?>) object, parent, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以上判断实现以下规范:
|
||||||
|
// - 实例类中仅加载 ConfigValue实例 与 Configuration实例
|
||||||
|
// - 静态类中仅加载 静态ConfigValue实例 与 静态Configuration类
|
||||||
|
|
||||||
|
} catch (IllegalAccessException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package cc.carm.lib.configuration.source.loader;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
public class PathGenerator {
|
||||||
|
|
||||||
|
public static PathGenerator of() {
|
||||||
|
return of(PathGenerator::covertPathName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PathGenerator of(UnaryOperator<String> pathConverter) {
|
||||||
|
return new PathGenerator(pathConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UnaryOperator<String> pathConverter;
|
||||||
|
|
||||||
|
public PathGenerator(UnaryOperator<String> pathConverter) {
|
||||||
|
this.pathConverter = pathConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull UnaryOperator<String> getPathConverter() {
|
||||||
|
return pathConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPathConverter(UnaryOperator<String> pathConverter) {
|
||||||
|
this.pathConverter = pathConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String covertPath(String name) {
|
||||||
|
return pathConverter.apply(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@Nullable String parentPath, @NotNull Field field) {
|
||||||
|
ConfigPath path = field.getAnnotation(ConfigPath.class);
|
||||||
|
if (path == null)
|
||||||
|
return link(holder, parentPath, false, covertPath(field.getName())); // No annotation, use field name.
|
||||||
|
else return link(holder, parentPath, path.root(), select(path.value(), covertPath(field.getName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String getClassPath(@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@Nullable String parentPath, @NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||||
|
// For standard path generator, we generate path following by:
|
||||||
|
// 1. Check if the class has a ConfigPath annotation, if so, use the root and value as the path.
|
||||||
|
// 2. If the class defined as a field, check if the field has a ConfigPath annotation,
|
||||||
|
// and use filed information.
|
||||||
|
ConfigPath clazzPath = clazz.getAnnotation(ConfigPath.class);
|
||||||
|
if (clazzPath != null) return link(holder, parentPath, clazzPath.root(), clazzPath.value());
|
||||||
|
|
||||||
|
if (clazzField == null) {
|
||||||
|
return link(holder, parentPath, false, parentPath == null ? null : covertPath(clazz.getSimpleName())); // No field, use class name.
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class);
|
||||||
|
if (fieldPath == null) return link(holder, parentPath, false, covertPath(clazzField.getName()));
|
||||||
|
else return getFieldPath(holder, parentPath, clazzField);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String select(String path, String defaultValue) {
|
||||||
|
if (path == null || path.isEmpty()) return defaultValue;
|
||||||
|
else return isBlank(path) ? null : path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected @Nullable String link(@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@Nullable String parent, boolean root, @Nullable String path) {
|
||||||
|
if (path == null || path.isEmpty()) return root ? null : parent;
|
||||||
|
return root || parent == null ? path : parent + pathSeparator(holder) + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBlank(String path) {
|
||||||
|
return path == null || path.replace(" ", "").isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static char pathSeparator(ConfigurationHolder<?> holder) {
|
||||||
|
return holder.options().get(StandardOptions.PATH_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configuration name of the specified element.
|
||||||
|
* Use the naming convention of all lowercase and "-" links.
|
||||||
|
* <p>
|
||||||
|
* e.g. "SOME_NAME" -> "some-name"
|
||||||
|
*
|
||||||
|
* @param name source name
|
||||||
|
* @return the final path
|
||||||
|
*/
|
||||||
|
public static String covertPathName(String name) {
|
||||||
|
return name
|
||||||
|
// Replace all uppercase letters with dashes
|
||||||
|
.replaceAll("[A-Z]", "=$0")
|
||||||
|
// If the first letter is also capitalized,
|
||||||
|
// it will also be converted and the first dash will need to be removed
|
||||||
|
.replaceAll("^=(.*)$", "$1")
|
||||||
|
// Because the name may contain _, it needs to be treated a little differently
|
||||||
|
.replaceAll("_=([A-Z])", "_$1")
|
||||||
|
// The content that is not named in all caps is then converted
|
||||||
|
.replaceAll("([a-z])=([A-Z])", "$1_$2")
|
||||||
|
// Remove any extra horizontal lines
|
||||||
|
.replaceAll("=", "")
|
||||||
|
// Replace the underscore with a dash
|
||||||
|
.replace("_", "-")
|
||||||
|
// Finally, convert it to all lowercase
|
||||||
|
.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+124
@@ -0,0 +1,124 @@
|
|||||||
|
package cc.carm.lib.configuration.source.meta;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ConfigurationMetaHolder {
|
||||||
|
|
||||||
|
protected final @NotNull Map<ConfigurationMetadata<?>, Object> values;
|
||||||
|
|
||||||
|
public ConfigurationMetaHolder() {
|
||||||
|
this(new ConcurrentHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationMetaHolder(@NotNull Map<ConfigurationMetadata<?>, Object> values) {
|
||||||
|
this.values = values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Map<ConfigurationMetadata<?>, Object> values() {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of option.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationMetadata}
|
||||||
|
* @param defaultValue Default value if the value of option is not set.
|
||||||
|
* @param <V> Value type
|
||||||
|
* @return Value of option
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Contract("_,!null -> !null")
|
||||||
|
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type, @Nullable V defaultValue) {
|
||||||
|
return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of option.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationMetadata}
|
||||||
|
* @param defaultValue Default value if the value of option is not set.
|
||||||
|
* @param <V> Value type
|
||||||
|
* @return Value of option
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Contract("_,!null -> !null")
|
||||||
|
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type, Supplier<@Nullable V> defaultValue) {
|
||||||
|
return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of option.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationMetadata}
|
||||||
|
* @param <V> Value type
|
||||||
|
* @return Value of option
|
||||||
|
*/
|
||||||
|
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type) {
|
||||||
|
return get(type, (V) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(@NotNull ConfigurationMetadata<?> type) {
|
||||||
|
return values().containsKey(type) || type.hasDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of meta, if the value is null, the meta will be removed.
|
||||||
|
* <br> Will only be changed in current holder.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationMetadata}
|
||||||
|
* @param value Value of meta
|
||||||
|
* @param <V> Value type
|
||||||
|
* @return Previous value of meta
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <V> @Nullable V set(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||||
|
if (value == null || type.isDefault(value)) {
|
||||||
|
return (V) values().remove(type);
|
||||||
|
} else {
|
||||||
|
return (V) values().put(type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of meta, if the value is null, the meta will not be changed.
|
||||||
|
* <br> Will only be changed in current holder.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationMetadata}
|
||||||
|
* @param value Value of meta
|
||||||
|
* @param <V> Value type
|
||||||
|
*/
|
||||||
|
public <V> void setIfAbsent(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||||
|
if (value == null || type.isDefault(value)) {
|
||||||
|
values().remove(type);
|
||||||
|
} else {
|
||||||
|
values().putIfAbsent(type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of meta, if the value is null, the meta will not be changed.
|
||||||
|
* <br> Will only be changed in current holder.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationMetadata}
|
||||||
|
* @param value Value of meta
|
||||||
|
* @param <V> Value type
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <V> @Nullable V setIfPresent(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||||
|
Object exists = values().get(type);
|
||||||
|
if (exists == null) return null;
|
||||||
|
|
||||||
|
if (value == null || type.isDefault(value)) {
|
||||||
|
return (V) values().remove(type);
|
||||||
|
} else {
|
||||||
|
return (V) values().put(type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package cc.carm.lib.configuration.source.meta;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ConfigurationMetadata<T> {
|
||||||
|
|
||||||
|
public static <T> ConfigurationMetadata<T> of() {
|
||||||
|
return of(() -> null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ConfigurationMetadata<T> of(T defaults) {
|
||||||
|
return of(() -> defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ConfigurationMetadata<T> of(@NotNull Supplier<@Nullable T> defaults) {
|
||||||
|
return new ConfigurationMetadata<>(defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Supplier<@Nullable T> defaultsSupplier;
|
||||||
|
|
||||||
|
public ConfigurationMetadata(@NotNull Supplier<@Nullable T> defaults) {
|
||||||
|
this.defaultsSupplier = defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDefault(@NotNull T value) {
|
||||||
|
return value.equals(defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDefaults() {
|
||||||
|
return defaults() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T defaultOrSupply(@Nullable T suppliedValue) {
|
||||||
|
return defaultOrSupply(() -> suppliedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T defaultOrSupply(Supplier<@Nullable T> suppliedValue) {
|
||||||
|
T defaults = defaults();
|
||||||
|
return defaults == null ? suppliedValue.get() : defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable T defaults() {
|
||||||
|
return defaultsSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaults(Supplier<T> defaultFunction) {
|
||||||
|
this.defaultsSupplier = defaultFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaults(T value) {
|
||||||
|
setDefaults(() -> value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package cc.carm.lib.configuration.source.option;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class ConfigurationOption<V> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> ConfigurationOption<T> of(@NotNull T defaultValue) {
|
||||||
|
return of((Class<T>) defaultValue.getClass(), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ConfigurationOption<T> of(@NotNull Class<T> valueClazz, @NotNull T defaultValue) {
|
||||||
|
return new ConfigurationOption<>(valueClazz, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final @NotNull Class<V> valueClazz;
|
||||||
|
private @NotNull V defaultValue;
|
||||||
|
|
||||||
|
public ConfigurationOption(@NotNull Class<V> valueClazz, @NotNull V defaultValue) {
|
||||||
|
this.valueClazz = valueClazz;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public Class<V> valueClass() {
|
||||||
|
return this.valueClazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull V defaults() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default value of option.
|
||||||
|
*
|
||||||
|
* @param defaultValue Default value
|
||||||
|
*/
|
||||||
|
public void defaults(@NotNull V defaultValue) {
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDefault(@NotNull V value) {
|
||||||
|
return value.equals(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
package cc.carm.lib.configuration.source.option;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class ConfigurationOptionHolder {
|
||||||
|
|
||||||
|
public static @NotNull ConfigurationOptionHolder of(@NotNull Map<ConfigurationOption<?>, Object> options) {
|
||||||
|
return new ConfigurationOptionHolder(new ConcurrentHashMap<>(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Map<ConfigurationOption<?>, Object> options;
|
||||||
|
|
||||||
|
public ConfigurationOptionHolder() {
|
||||||
|
this(new ConcurrentHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationOptionHolder(Map<ConfigurationOption<?>, Object> options) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Map<ConfigurationOption<?>, Object> values() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of option.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationOption}
|
||||||
|
* @param <V> Value type
|
||||||
|
* @return Value of option
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <V> @NotNull V get(@NotNull ConfigurationOption<V> type) {
|
||||||
|
return Optional.ofNullable(values().get(type)).map(v -> (V) v).orElseGet(type::defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of option.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationOption}
|
||||||
|
* @param value Value of option
|
||||||
|
* @param <V> Value type
|
||||||
|
* @return Previous value of option
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <V> @Nullable V set(@NotNull ConfigurationOption<V> type, @Nullable V value) {
|
||||||
|
if (value == null) {
|
||||||
|
return (V) values().remove(type);
|
||||||
|
} else {
|
||||||
|
return (V) values().put(type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of option to option's {@link ConfigurationOption#defaults()}.
|
||||||
|
*
|
||||||
|
* @param type {@link ConfigurationOption}
|
||||||
|
* @param <V> Value type
|
||||||
|
* @return Previous value of option
|
||||||
|
*/
|
||||||
|
public <V> @Nullable V clear(@NotNull ConfigurationOption<V> type) {
|
||||||
|
return set(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package cc.carm.lib.configuration.source.option;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
|
||||||
|
import static cc.carm.lib.configuration.source.option.ConfigurationOption.of;
|
||||||
|
|
||||||
|
public interface StandardOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration path separator.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Character> PATH_SEPARATOR = of('.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to set & save default values if offered and not exists in configuration.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> SET_DEFAULTS = of(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to load subclasses of configuration class.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> LOAD_SUB_CLASSES = of(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to pre parse the config values.
|
||||||
|
* <br> if false, the values will be parsed when calling
|
||||||
|
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
|
||||||
|
* <br> if true, the values will be parsed when
|
||||||
|
* {@link ConfigurationHolder#initialize(Configuration)}.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> PRELOAD = of(false);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public interface ConfigureSection {
|
||||||
|
|
||||||
|
@NotNull ConfigureSource<?, ?, ?> source();
|
||||||
|
|
||||||
|
@Nullable ConfigureSection parent();
|
||||||
|
|
||||||
|
default char separator() {
|
||||||
|
return source().holder().options().get(StandardOptions.PATH_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
default Set<String> getKeys(boolean deep) {
|
||||||
|
return getValues(deep).keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
Map<String, Object> getValues(boolean deep);
|
||||||
|
|
||||||
|
void set(@NotNull String path, @Nullable Object value);
|
||||||
|
|
||||||
|
boolean contains(@NotNull String path);
|
||||||
|
|
||||||
|
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
||||||
|
return typeClass.isInstance(get(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isList(@NotNull String path);
|
||||||
|
|
||||||
|
@Nullable List<?> getList(@NotNull String path);
|
||||||
|
|
||||||
|
boolean isSection(@NotNull String path);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ConfigureSection getSection(@NotNull String path);
|
||||||
|
|
||||||
|
@Nullable Object get(@NotNull String path);
|
||||||
|
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> clazz) {
|
||||||
|
return get(path, null, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<Object, T> parser) {
|
||||||
|
return get(path, null, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_,!null,_->!null")
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
|
||||||
|
return get(path, defaultValue, DataFunction.castObject(clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_,!null,_->!null")
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
||||||
|
@NotNull DataFunction<Object, T> parser) {
|
||||||
|
Object value = get(path);
|
||||||
|
if (value != null) {
|
||||||
|
try {
|
||||||
|
return parser.handle(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean isBoolean(@NotNull String path) {
|
||||||
|
return isType(path, Boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean getBoolean(@NotNull String path) {
|
||||||
|
return getBoolean(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
||||||
|
return get(path, def, DataFunction.booleanValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Boolean isByte(@NotNull String path) {
|
||||||
|
return isType(path, Byte.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Byte getByte(@NotNull String path) {
|
||||||
|
return getByte(path, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
||||||
|
return get(path, def, DataFunction.byteValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isShort(@NotNull String path) {
|
||||||
|
return isType(path, Short.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Short getShort(@NotNull String path) {
|
||||||
|
return getShort(path, (short) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
||||||
|
return get(path, def, DataFunction.shortValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean isInt(@NotNull String path) {
|
||||||
|
return isType(path, Integer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Integer getInt(@NotNull String path) {
|
||||||
|
return getInt(path, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
||||||
|
return get(path, def, DataFunction.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean isLong(@NotNull String path) {
|
||||||
|
return isType(path, Long.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Long getLong(@NotNull String path) {
|
||||||
|
return getLong(path, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
||||||
|
return get(path, def, DataFunction.longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean isFloat(@NotNull String path) {
|
||||||
|
return isType(path, Float.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Float getFloat(@NotNull String path) {
|
||||||
|
return getFloat(path, 0.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
||||||
|
return get(path, def, DataFunction.floatValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean isDouble(@NotNull String path) {
|
||||||
|
return isType(path, Double.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Double getDouble(@NotNull String path) {
|
||||||
|
return getDouble(path, 0.0D);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
||||||
|
return get(path, def, DataFunction.doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean isChar(@NotNull String path) {
|
||||||
|
return isType(path, Boolean.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable Character getChar(@NotNull String path) {
|
||||||
|
return getChar(path, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
||||||
|
return get(path, def, Character.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean isString(@NotNull String path) {
|
||||||
|
return isType(path, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @Nullable String getString(@NotNull String path) {
|
||||||
|
return getString(path, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Contract("_, !null -> !null")
|
||||||
|
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
||||||
|
return get(path, def, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of values from the section
|
||||||
|
* <p>
|
||||||
|
* If the path does not exist, an empty list will be returned
|
||||||
|
* <br>Any changes please use {@link #set(String, Object)} after changes
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @param parser The function to parse the values
|
||||||
|
* @param <V> The type of the values
|
||||||
|
* @return The list of values
|
||||||
|
*/
|
||||||
|
default <V> @NotNull List<V> getList(@NotNull String path, @NotNull DataFunction<Object, V> parser) {
|
||||||
|
return getCollection(path, ArrayList::new, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of strings from the section
|
||||||
|
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @return The list of strings
|
||||||
|
*/
|
||||||
|
default @NotNull List<String> getStringList(@NotNull String path) {
|
||||||
|
return getList(path, DataFunction.castToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of integer from the section
|
||||||
|
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @return The list of int values
|
||||||
|
*/
|
||||||
|
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
|
||||||
|
return getList(path, DataFunction.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of long from the section
|
||||||
|
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @return The list of long values
|
||||||
|
*/
|
||||||
|
default @NotNull List<Long> getLongList(@NotNull String path) {
|
||||||
|
return getList(path, DataFunction.longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of double from the section
|
||||||
|
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @return The list of doubles
|
||||||
|
*/
|
||||||
|
default @NotNull List<Double> getDoubleList(@NotNull String path) {
|
||||||
|
return getList(path, DataFunction.doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of floats from the section
|
||||||
|
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @return The list of floats
|
||||||
|
*/
|
||||||
|
default @NotNull List<Float> getFloatList(@NotNull String path) {
|
||||||
|
return getList(path, DataFunction.floatValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of bytes from the section
|
||||||
|
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @return The list of bytes
|
||||||
|
*/
|
||||||
|
default @NotNull List<Byte> getByteList(@NotNull String path) {
|
||||||
|
return getList(path, DataFunction.byteValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of char from the section
|
||||||
|
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||||
|
*
|
||||||
|
* @param path The path to get the list from
|
||||||
|
* @return The list of char
|
||||||
|
*/
|
||||||
|
default @NotNull List<Character> getCharList(@NotNull String path) {
|
||||||
|
return getList(path, DataFunction.castObject(Character.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path,
|
||||||
|
@NotNull Supplier<C> constructor,
|
||||||
|
@NotNull DataFunction<Object, T> parser) {
|
||||||
|
return parseCollection(getList(path), constructor, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
default @NotNull Stream<?> stream(@NotNull String path) {
|
||||||
|
List<?> values = getList(path);
|
||||||
|
return values == null ? Stream.empty() : values.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T> @NotNull Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) {
|
||||||
|
return stream(path).map(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T, C extends Collection<T>> @NotNull C parseCollection(
|
||||||
|
@Nullable List<?> data, @NotNull Supplier<C> constructor,
|
||||||
|
@NotNull DataFunction<Object, T> parser
|
||||||
|
) {
|
||||||
|
C values = constructor.get();
|
||||||
|
if (data == null) return values;
|
||||||
|
for (Object obj : data) {
|
||||||
|
try {
|
||||||
|
values.add(parser.handle(obj));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class ConfigureSource<
|
||||||
|
SECTION extends ConfigureSection, ORIGINAL,
|
||||||
|
SELF extends ConfigureSource<SECTION, ORIGINAL, SELF>>
|
||||||
|
implements ConfigureSection {
|
||||||
|
|
||||||
|
protected final @NotNull ConfigurationHolder<? extends SELF> holder;
|
||||||
|
protected long lastUpdateMillis;
|
||||||
|
|
||||||
|
protected ConfigureSource(@NotNull ConfigurationHolder<? extends SELF> holder, long lastUpdateMillis) {
|
||||||
|
this.holder = holder;
|
||||||
|
this.lastUpdateMillis = lastUpdateMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigurationHolder<? extends SELF> holder() {
|
||||||
|
return holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void reload() throws Exception {
|
||||||
|
onReload(); // 调用重写的Reload方法
|
||||||
|
this.lastUpdateMillis = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract SELF self();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Original configuration object
|
||||||
|
*/
|
||||||
|
public abstract @NotNull ORIGINAL original();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The root {@link ConfigureSection}
|
||||||
|
*/
|
||||||
|
public abstract @NotNull SECTION section();
|
||||||
|
|
||||||
|
public abstract void save() throws Exception;
|
||||||
|
|
||||||
|
protected abstract void onReload() throws Exception;
|
||||||
|
|
||||||
|
public long getLastUpdateMillis() {
|
||||||
|
return this.lastUpdateMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExpired(long parsedTime) {
|
||||||
|
return getLastUpdateMillis() > parsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigureSection parent() {
|
||||||
|
return null; // Source also represents the root section, so it has no parent
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||||
|
return section().getValues(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
|
section().set(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull String path) {
|
||||||
|
return section().contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isList(@NotNull String path) {
|
||||||
|
return section().isList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable List<?> getList(@NotNull String path) {
|
||||||
|
return section().getList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSection(@NotNull String path) {
|
||||||
|
return section().isSection(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
||||||
|
return section().getSection(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object get(@NotNull String path) {
|
||||||
|
return section().get(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package cc.carm.lib.configuration.value;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class ConfigValue<T> extends ValueManifest<T> {
|
||||||
|
|
||||||
|
protected ConfigValue(@NotNull ValueManifest<T> manifest) {
|
||||||
|
super(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到该配置的设定值(即读取到的值)。
|
||||||
|
* <br> 若初始化时未写入默认值,则可以通过 {@link #getOrDefault()} 方法在该设定值为空时获取默认值。
|
||||||
|
*
|
||||||
|
* @return 设定值
|
||||||
|
*/
|
||||||
|
public abstract @Nullable T get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到该配置的设定值,若不存在,则返回默认值。
|
||||||
|
*
|
||||||
|
* @return 设定值或默认值
|
||||||
|
*/
|
||||||
|
public T getOrDefault() {
|
||||||
|
return optional().orElse(defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 得到该配置的非空值。
|
||||||
|
*
|
||||||
|
* @return 非空值
|
||||||
|
* @throws NullPointerException 对应数据为空时抛出
|
||||||
|
*/
|
||||||
|
public @NotNull T getNotNull() {
|
||||||
|
return Objects.requireNonNull(getOrDefault(), "Value(" + type() + ") @[" + path() + "] is null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Optional<@Nullable T> optional() {
|
||||||
|
return Optional.ofNullable(get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设定该配置的值。
|
||||||
|
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。
|
||||||
|
*
|
||||||
|
* @param value 配置的值
|
||||||
|
*/
|
||||||
|
public abstract void set(@Nullable T value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化该配置的默认值。
|
||||||
|
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。
|
||||||
|
*/
|
||||||
|
public void setDefault() {
|
||||||
|
setDefault(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将该配置的值设置为默认值。
|
||||||
|
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。
|
||||||
|
*
|
||||||
|
* @param override 是否覆盖已设定的值
|
||||||
|
*/
|
||||||
|
public void setDefault(boolean override) {
|
||||||
|
if (!override && config().contains(path())) return;
|
||||||
|
Optional.ofNullable(defaults()).ifPresent(this::set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断加载的配置是否与默认值相同。
|
||||||
|
*
|
||||||
|
* @return 获取当前值是否为默认值。
|
||||||
|
*/
|
||||||
|
public boolean isDefault() {
|
||||||
|
return Objects.equals(defaults(), get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to save the configuration.
|
||||||
|
* <br>To save multiple modifications,
|
||||||
|
* it is recommended to call {@link ConfigurationHolder#save()}
|
||||||
|
* after all modifications are completed instead of this.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public void save() throws Exception {
|
||||||
|
holder().save();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package cc.carm.lib.configuration.value;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ValueManifest<T> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<T> type;
|
||||||
|
protected final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer;
|
||||||
|
|
||||||
|
protected @Nullable ConfigurationHolder<?> holder;
|
||||||
|
protected @Nullable String path; // Section path
|
||||||
|
|
||||||
|
protected @NotNull Supplier<@Nullable T> defaultSupplier;
|
||||||
|
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<T> type) {
|
||||||
|
this(type, () -> null, EMPTY_INITIALIZER, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull T defaultValue) {
|
||||||
|
this(ValueType.of(defaultValue), () -> defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier) {
|
||||||
|
this(type, defaultSupplier, EMPTY_INITIALIZER, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
|
||||||
|
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer) {
|
||||||
|
this(type, defaultSupplier, initializer, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
|
||||||
|
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer,
|
||||||
|
@Nullable ConfigurationHolder<?> holder, @Nullable String path) {
|
||||||
|
this.type = type;
|
||||||
|
this.initializer = initializer;
|
||||||
|
this.defaultSupplier = defaultSupplier;
|
||||||
|
this.holder = holder;
|
||||||
|
this.path = path;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ValueManifest(@NotNull ValueManifest<T> manifest) {
|
||||||
|
this(manifest.type, manifest.defaultSupplier, manifest.initializer, manifest.holder, manifest.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(@NotNull ConfigurationHolder<?> holder, @NotNull String path) {
|
||||||
|
this.holder = holder;
|
||||||
|
this.path = path;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initialize() {
|
||||||
|
if (holder != null && path != null) this.initializer.accept(holder, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueType<T> type() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void holder(@NotNull ConfigurationHolder<?> holder) {
|
||||||
|
this.holder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void path(@NotNull String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable T defaults() {
|
||||||
|
return this.defaultSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaults(@Nullable T defaultValue) {
|
||||||
|
defaults(() -> defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaults(@NotNull Supplier<@Nullable T> defaultValue) {
|
||||||
|
this.defaultSupplier = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDefaults() {
|
||||||
|
return this.defaultSupplier.get() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String path() {
|
||||||
|
if (path != null) return path;
|
||||||
|
else throw new IllegalStateException("No section path provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigurationHolder<?> holder() {
|
||||||
|
if (this.holder != null) return this.holder;
|
||||||
|
throw new IllegalStateException("Value does not have a provider.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigureSource<?, ?, ?> config() {
|
||||||
|
return holder().config();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationMetaHolder metadata() {
|
||||||
|
return holder().metadata(path());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getData() {
|
||||||
|
return config().get(path());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setData(@Nullable Object value) {
|
||||||
|
config().set(path(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, path) -> {
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package cc.carm.lib.configuration.value.impl;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
|
||||||
|
|
||||||
|
protected @Nullable T cachedValue;
|
||||||
|
protected long parsedTime = -1;
|
||||||
|
|
||||||
|
protected CachedConfigValue(@NotNull ValueManifest<T> manifest) {
|
||||||
|
super(manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected T updateCache(T value) {
|
||||||
|
this.parsedTime = System.currentTimeMillis();
|
||||||
|
this.cachedValue = value;
|
||||||
|
return getCachedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable T getCachedValue() {
|
||||||
|
return cachedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean cacheExpired() {
|
||||||
|
return this.parsedTime <= 0 || config().isExpired(this.parsedTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final T getDefaultFirst(@Nullable T value) {
|
||||||
|
return updateCache(this.defaults() == null ? value : this.defaults());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cached value or the default value if the cached value is null
|
||||||
|
*
|
||||||
|
* @return the cached value or the default value
|
||||||
|
*/
|
||||||
|
protected @Nullable T getCachedOrDefault() {
|
||||||
|
return getCachedOrDefault(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cached value or the default value if the cached value is null
|
||||||
|
*
|
||||||
|
* @param emptyValue the value to return if the cached value and the default value are null
|
||||||
|
* @return the cached value or the default value
|
||||||
|
*/
|
||||||
|
@Contract("!null->!null")
|
||||||
|
protected T getCachedOrDefault(@Nullable T emptyValue) {
|
||||||
|
if (getCachedValue() != null) return getCachedValue();
|
||||||
|
else if (defaults() != null) return defaults();
|
||||||
|
else return emptyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value's parser, parse base object to value.
|
||||||
|
*/
|
||||||
|
protected <O> @Nullable ValueParser<O> parserFor(@NotNull ValueAdapter<O> adapter) {
|
||||||
|
if (adapter.parser() != null) return adapter.parser();
|
||||||
|
ValueAdapter<O> registered = holder().adapters().adapterOf(adapter.type());
|
||||||
|
if (registered == null) return null;
|
||||||
|
return registered.parser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value's serializer, parse value to base object.
|
||||||
|
*/
|
||||||
|
protected <O> @Nullable ValueSerializer<O> serializerFor(@NotNull ValueAdapter<O> adapter) {
|
||||||
|
if (adapter.serializer() != null) return adapter.serializer();
|
||||||
|
ValueAdapter<O> registered = holder().adapters().adapterOf(adapter.type());
|
||||||
|
if (registered == null) return null;
|
||||||
|
return registered.serializer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
package cc.carm.lib.configuration.value.standard;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.list.ConfigListBuilder;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements List<V> {
|
||||||
|
|
||||||
|
public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull Class<T> type) {
|
||||||
|
return builderOf(ValueType.of(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull ValueType<T> type) {
|
||||||
|
return new ConfigListBuilder<>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @NotNull Supplier<? extends List<V>> constructor;
|
||||||
|
protected final @NotNull ValueAdapter<V> paramAdapter;
|
||||||
|
|
||||||
|
public ConfiguredList(@NotNull ValueManifest<List<V>> manifest,
|
||||||
|
@NotNull Supplier<? extends List<V>> constructor,
|
||||||
|
@NotNull ValueAdapter<V> paramAdapter) {
|
||||||
|
super(manifest);
|
||||||
|
this.constructor = constructor;
|
||||||
|
this.paramAdapter = paramAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Adapter of this value.
|
||||||
|
*/
|
||||||
|
public @NotNull ValueAdapter<V> adapter() {
|
||||||
|
return this.paramAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueType<V> paramType() {
|
||||||
|
return adapter().type();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value's parser, parse base object to value.
|
||||||
|
*/
|
||||||
|
public @Nullable ValueParser<V> parser() {
|
||||||
|
return parserFor(adapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value's serializer, parse value to base object.
|
||||||
|
*/
|
||||||
|
public @Nullable ValueSerializer<V> serializer() {
|
||||||
|
return serializerFor(adapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull List<V> createList() {
|
||||||
|
return constructor.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<V> get() {
|
||||||
|
if (!cacheExpired()) return getCachedOrDefault(createList());
|
||||||
|
// Data that is outdated and needs to be parsed again.
|
||||||
|
List<V> list = createList();
|
||||||
|
List<?> data = config().contains(path()) ? config().getList(path()) : null;
|
||||||
|
if (data == null) return getDefaultFirst(list);
|
||||||
|
|
||||||
|
ValueParser<V> parser = parser();
|
||||||
|
if (parser == null) return getDefaultFirst(list);
|
||||||
|
|
||||||
|
for (Object dataVal : data) {
|
||||||
|
if (dataVal == null) continue;
|
||||||
|
try {
|
||||||
|
list.add(parser.parse(holder(), paramType(), dataVal));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updateCache(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@Nullable List<V> value) {
|
||||||
|
updateCache(value);
|
||||||
|
if (value == null) {
|
||||||
|
setData(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueSerializer<V> serializer = serializer();
|
||||||
|
if (serializer == null) return;
|
||||||
|
List<Object> data = new ArrayList<>();
|
||||||
|
for (V val : value) {
|
||||||
|
if (val == null) continue;
|
||||||
|
try {
|
||||||
|
data.add(serializer.serialize(holder(), paramType(), val));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(int index) {
|
||||||
|
return getNotNull().get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull List<V> copy() {
|
||||||
|
return new ArrayList<>(getNotNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> @NotNull T handle(Function<List<V>, T> function) {
|
||||||
|
List<V> list = getNotNull();
|
||||||
|
T result = function.apply(list);
|
||||||
|
set(list);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfiguredList<V> modify(Consumer<List<V>> consumer) {
|
||||||
|
List<V> list = getNotNull();
|
||||||
|
consumer.accept(list);
|
||||||
|
set(list);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V set(int index, V element) {
|
||||||
|
return handle(list -> list.set(index, element));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return getNotNull().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return getNotNull().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return getNotNull().contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return getNotNull().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Object @NotNull [] toArray() {
|
||||||
|
return getNotNull().toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> T @NotNull [] toArray(@NotNull T[] a) {
|
||||||
|
return getNotNull().toArray(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(@NotNull Collection<?> c) {
|
||||||
|
return new HashSet<>(getNotNull()).containsAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(V v) {
|
||||||
|
handle(list -> list.add(v));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, V element) {
|
||||||
|
modify(list -> list.add(index, element));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(@NotNull Collection<? extends V> c) {
|
||||||
|
return handle(list -> list.addAll(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int index, @NotNull Collection<? extends V> c) {
|
||||||
|
return handle(list -> list.addAll(index, c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
return handle(list -> list.remove(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(int index) {
|
||||||
|
return handle(list -> list.remove(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(@NotNull Collection<?> c) {
|
||||||
|
return handle(list -> list.removeAll(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(@NotNull Collection<?> c) {
|
||||||
|
return handle(list -> list.retainAll(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
modify(List::clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
return getNotNull().indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
return getNotNull().lastIndexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ListIterator<V> listIterator() {
|
||||||
|
return getNotNull().listIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ListIterator<V> listIterator(int index) {
|
||||||
|
return getNotNull().listIterator(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public List<V> subList(int fromIndex, int toIndex) {
|
||||||
|
return getNotNull().subList(fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
package cc.carm.lib.configuration.value.standard;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements Map<K, V> {
|
||||||
|
|
||||||
|
protected final @NotNull Supplier<? extends Map<K, V>> constructor;
|
||||||
|
|
||||||
|
protected final @NotNull ValueAdapter<K> keyAdapter;
|
||||||
|
protected final @NotNull ValueAdapter<V> valueAdapter;
|
||||||
|
|
||||||
|
protected ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest,
|
||||||
|
@NotNull Supplier<? extends Map<K, V>> constructor,
|
||||||
|
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
|
||||||
|
super(manifest);
|
||||||
|
this.constructor = constructor;
|
||||||
|
this.keyAdapter = keyAdapter;
|
||||||
|
this.valueAdapter = valueAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueAdapter<K> keyAdapter() {
|
||||||
|
return keyAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueType<K> keyType() {
|
||||||
|
return keyAdapter().type();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueAdapter<V> valueAdapter() {
|
||||||
|
return valueAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueType<V> valueType() {
|
||||||
|
return valueAdapter().type();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<K, V> createMap() {
|
||||||
|
return this.constructor.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<K, V> get() {
|
||||||
|
if (!cacheExpired()) return getCachedOrDefault(createMap());
|
||||||
|
// If the value is expired, we need to update it
|
||||||
|
Map<K, V> map = createMap();
|
||||||
|
|
||||||
|
ConfigureSection section = config().getSection(path());
|
||||||
|
if (section == null) return getDefaultFirst(map);
|
||||||
|
|
||||||
|
Set<String> keys = section.getKeys(false);
|
||||||
|
if (keys.isEmpty()) return getDefaultFirst(map);
|
||||||
|
|
||||||
|
ValueParser<K> keyParser = parserFor(keyAdapter);
|
||||||
|
ValueParser<V> valueParser = parserFor(valueAdapter);
|
||||||
|
if (keyParser == null || valueParser == null) return getDefaultFirst(map);
|
||||||
|
|
||||||
|
for (String dataKey : keys) {
|
||||||
|
Object dataVal = section.get(dataKey);
|
||||||
|
if (dataVal == null) continue;
|
||||||
|
try {
|
||||||
|
K key = keyParser.parse(holder(), keyType(), dataKey);
|
||||||
|
V value = valueParser.parse(holder(), valueType(), dataVal);
|
||||||
|
map.put(key, value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateCache(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get(Object key) {
|
||||||
|
return get().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public V getNotNull(Object key) {
|
||||||
|
return Objects.requireNonNull(get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@Nullable Map<K, V> value) {
|
||||||
|
updateCache(value);
|
||||||
|
if (value == null) {
|
||||||
|
setData(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
|
||||||
|
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
|
||||||
|
if (keySerializer == null || valueSerializer == null) return;
|
||||||
|
|
||||||
|
Map<Object, Object> data = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<K, V> entry : value.entrySet()) {
|
||||||
|
try {
|
||||||
|
data.put(
|
||||||
|
keySerializer.serialize(holder(), keyType(), entry.getKey()),
|
||||||
|
valueSerializer.serialize(holder(), valueType(), entry.getValue())
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> @NotNull T handle(Function<Map<K, V>, T> function) {
|
||||||
|
Map<K, V> m = get();
|
||||||
|
T result = function.apply(m);
|
||||||
|
set(m);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfiguredMap<K, V> modify(Consumer<Map<K, V>> consumer) {
|
||||||
|
Map<K, V> m = get();
|
||||||
|
consumer.accept(m);
|
||||||
|
set(m);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return get().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return get().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object key) {
|
||||||
|
return get().containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object value) {
|
||||||
|
return get().containsValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public V put(K key, V value) {
|
||||||
|
return handle(m -> m.put(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V remove(Object key) {
|
||||||
|
return handle(m -> m.remove(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
|
||||||
|
modify(map -> map.putAll(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
modify(Map::clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<K> keySet() {
|
||||||
|
return get().keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
return get().values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
@Unmodifiable
|
||||||
|
public Set<Entry<K, V>> entrySet() {
|
||||||
|
return get().entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package cc.carm.lib.configuration.value.standard;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.value.ConfigValueBuilder;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
||||||
|
|
||||||
|
public static <V> ConfigValueBuilder<V> builderOf(@NotNull Class<V> type) {
|
||||||
|
return new ConfigValueBuilder<>(ValueType.of(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfigValueBuilder<V> builderOf(@NotNull ValueType<V> type) {
|
||||||
|
return new ConfigValueBuilder<>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull V defaults) {
|
||||||
|
return of(ValueType.of(defaults), () -> defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type) {
|
||||||
|
return of(ValueType.of(type), () -> null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type, @NotNull V defaults) {
|
||||||
|
return of(ValueType.of(type), () -> defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type, @NotNull Supplier<@Nullable V> defaultSupplier) {
|
||||||
|
return of(ValueType.of(type), defaultSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull ValueType<V> type) {
|
||||||
|
return of(type, () -> null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull ValueType<V> type, @NotNull Supplier<@Nullable V> defaultSupplier) {
|
||||||
|
return of(
|
||||||
|
new ValueManifest<>(type, defaultSupplier),
|
||||||
|
(provider, t, data) -> provider.deserialize(type, data),
|
||||||
|
(provider, t, value) -> provider.serialize(value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest,
|
||||||
|
@Nullable ValueParser<V> parser,
|
||||||
|
@Nullable ValueSerializer<V> serializer) {
|
||||||
|
ValueAdapter<V> adapter = new ValueAdapter<>(manifest.type());
|
||||||
|
adapter.parser(parser);
|
||||||
|
adapter.serializer(serializer);
|
||||||
|
return of(manifest, adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) {
|
||||||
|
return new ConfiguredValue<>(manifest, adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @NotNull ValueAdapter<V> adapter;
|
||||||
|
|
||||||
|
public ConfiguredValue(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) {
|
||||||
|
super(manifest);
|
||||||
|
this.adapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Adapter of this value.
|
||||||
|
*/
|
||||||
|
public @NotNull ValueAdapter<V> adapter() {
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value's parser, parse base object to value.
|
||||||
|
*/
|
||||||
|
public @Nullable ValueParser<V> parser() {
|
||||||
|
return parserFor(adapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value's serializer, parse value to base object.
|
||||||
|
*/
|
||||||
|
public @Nullable ValueSerializer<V> serializer() {
|
||||||
|
return serializerFor(adapter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V get() {
|
||||||
|
if (!cacheExpired()) return getCachedOrDefault();
|
||||||
|
// Data that is outdated and needs to be parsed again.
|
||||||
|
|
||||||
|
Object data = getData();
|
||||||
|
if (data == null) return defaults();
|
||||||
|
|
||||||
|
ValueParser<V> parser = parser();
|
||||||
|
if (parser == null) return defaults(); // No parser, return default value.
|
||||||
|
|
||||||
|
try {
|
||||||
|
// If there are no errors, update the cache and return.
|
||||||
|
return updateCache(parser.parse(holder(), type(), data));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// There was a parsing error, prompted and returned the default value.
|
||||||
|
e.printStackTrace();
|
||||||
|
return defaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value of the configuration path.
|
||||||
|
* Will use {@link #serializer()} to serialize the value.
|
||||||
|
*
|
||||||
|
* @param value The value to be set
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void set(V value) {
|
||||||
|
updateCache(value); // Update cache
|
||||||
|
if (value == null) {
|
||||||
|
setData(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueSerializer<V> serializer = serializer();
|
||||||
|
if (serializer == null) return; // No serializer, do nothing.
|
||||||
|
|
||||||
|
try {
|
||||||
|
setData(serializer.serialize(holder(), type(), value));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||||
|
import cc.carm.test.config.TestSource;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class AdaptTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception {
|
||||||
|
|
||||||
|
ValueAdapterRegistry registry = new ValueAdapterRegistry();
|
||||||
|
registry.register(PrimitiveAdapter.ADAPTERS);
|
||||||
|
registry.register(PrimitiveAdapter.ofEnum());
|
||||||
|
|
||||||
|
|
||||||
|
registry.register(ValueType.of(Long.class), ValueType.of(Duration.class), Duration::ofMillis, Duration::toMillis);
|
||||||
|
registry.register(
|
||||||
|
ValueType.of(Duration.class), ValueType.of(LocalTime.class),
|
||||||
|
duration -> LocalTime.now().plus(duration),
|
||||||
|
data -> Duration.between(LocalTime.now(), data)
|
||||||
|
);
|
||||||
|
|
||||||
|
ConfigurationHolder<TestSource> provider = new ConfigurationHolder<TestSource>(
|
||||||
|
registry, new ConfigurationOptionHolder(),
|
||||||
|
new ConcurrentHashMap<>(), new ConfigurationInitializer()
|
||||||
|
) {
|
||||||
|
final TestSource source = new TestSource(this, System.currentTimeMillis());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull TestSource config() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalTime v = registry.deserialize(provider, LocalTime.class, 600000L);
|
||||||
|
Object d = registry.serialize(provider, v);
|
||||||
|
|
||||||
|
System.out.println(v);
|
||||||
|
System.out.println(d);
|
||||||
|
System.out.println(registry.deserialize(provider, TestEnum.class, "C"));
|
||||||
|
System.out.println(registry.serialize(provider, TestEnum.C).getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TestEnum {
|
||||||
|
A, b, C
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
import cc.carm.lib.configuration.source.loader.PathGenerator;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class NameTest {
|
public class NameTest {
|
||||||
@@ -7,10 +7,10 @@ public class NameTest {
|
|||||||
@Test
|
@Test
|
||||||
public void onTest() {
|
public void onTest() {
|
||||||
|
|
||||||
System.out.println(ConfigInitializer.getPathFromName("LoveGames")); // -> love-games
|
System.out.println(PathGenerator.covertPathName("LoveGames")); // -> love-games
|
||||||
System.out.println(ConfigInitializer.getPathFromName("EASY_GAME")); // -> easy-game
|
System.out.println(PathGenerator.covertPathName("EASY_GAME")); // -> easy-game
|
||||||
System.out.println(ConfigInitializer.getPathFromName("F")); //-? f
|
System.out.println(PathGenerator.covertPathName("F")); //-? f
|
||||||
System.out.println(ConfigInitializer.getPathFromName("Test123123")); // -? test123123123
|
System.out.println(PathGenerator.covertPathName("Test123123")); // -? test123123123
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package cc.carm.test.config;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||||
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class LoaderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception {
|
||||||
|
ConfigurationHolder<TestSource> provider = new ConfigurationHolder<TestSource>(
|
||||||
|
new ValueAdapterRegistry(), new ConfigurationOptionHolder(),
|
||||||
|
new ConcurrentHashMap<>(), new ConfigurationInitializer()
|
||||||
|
) {
|
||||||
|
final TestSource source = new TestSource(this, System.currentTimeMillis());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull TestSource config() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigurationInitializer loader = new ConfigurationInitializer();
|
||||||
|
loader.initialize(provider, ROOT.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ROOT extends Configuration {
|
||||||
|
|
||||||
|
interface SUB extends Configuration {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ConfigPath(root = true)
|
||||||
|
interface EXTERNAL extends Configuration {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigPath("NO")
|
||||||
|
interface YES extends Configuration {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package cc.carm.test.config;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TestSection implements ConfigureSection {
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfigureSource<?, ?, ?> source() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull String path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isList(@NotNull String path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable List<?> getList(@NotNull String path) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSection(@NotNull String path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object get(@NotNull String path) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package cc.carm.test.config;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class TestSource extends ConfigureSource<TestSection, Map<String, String>, TestSource> {
|
||||||
|
|
||||||
|
|
||||||
|
public TestSource(@NotNull ConfigurationHolder<? extends TestSource> holder, long lastUpdateMillis) {
|
||||||
|
super(holder, lastUpdateMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TestSource self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onReload() throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<String, String> original() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull TestSection section() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfigureSource<?, ?, ?> source() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,13 @@
|
|||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-feature-commentable</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package cc.carm.lib.configuration.demo;
|
package cc.carm.lib.configuration.demo;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
import cc.carm.lib.configuration.Configuration;
|
||||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
|
||||||
@HeaderComment({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
@HeaderComment({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
||||||
public class DatabaseConfiguration extends ConfigurationRoot {
|
public class DatabaseConfiguration implements Configuration {
|
||||||
|
|
||||||
@ConfigPath("driver")
|
@ConfigPath("driver")
|
||||||
@HeaderComment({
|
@HeaderComment({
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests;
|
package cc.carm.lib.configuration.demo.tests;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
|
import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
|
||||||
import cc.carm.lib.configuration.demo.tests.conf.TestConfiguration;
|
import cc.carm.lib.configuration.demo.tests.conf.RegistryConfig;
|
||||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
import org.jetbrains.annotations.TestOnly;
|
import org.jetbrains.annotations.TestOnly;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -15,8 +14,12 @@ import java.util.stream.IntStream;
|
|||||||
public class ConfigurationTest {
|
public class ConfigurationTest {
|
||||||
|
|
||||||
@TestOnly
|
@TestOnly
|
||||||
public static void testDemo(ConfigurationProvider<?> provider) {
|
public static void testDemo(ConfigurationHolder<?> holder) {
|
||||||
provider.initialize(DemoConfiguration.class);
|
try {
|
||||||
|
holder.initialize(DemoConfiguration.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
System.out.println("----------------------------------------------------");
|
System.out.println("----------------------------------------------------");
|
||||||
|
|
||||||
@@ -27,57 +30,57 @@ public class ConfigurationTest {
|
|||||||
System.out.println("after: " + DemoConfiguration.TEST_NUMBER.get());
|
System.out.println("after: " + DemoConfiguration.TEST_NUMBER.get());
|
||||||
|
|
||||||
System.out.println("> Test Value:");
|
System.out.println("> Test Value:");
|
||||||
System.out.println("before: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
System.out.println("before: " + DemoConfiguration.SUB.UUID_CONFIG_VALUE.get());
|
||||||
DemoConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID());
|
DemoConfiguration.SUB.UUID_CONFIG_VALUE.set(UUID.randomUUID());
|
||||||
System.out.println("after: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
System.out.println("after: " + DemoConfiguration.SUB.UUID_CONFIG_VALUE.get());
|
||||||
|
|
||||||
System.out.println("> Test List:");
|
System.out.println("> Test List:");
|
||||||
|
|
||||||
System.out.println(" Before:");
|
System.out.println(" Before:");
|
||||||
DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println);
|
DemoConfiguration.SUB.That.OPERATORS.forEach(System.out::println);
|
||||||
List<UUID> operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList());
|
List<UUID> operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList());
|
||||||
DemoConfiguration.Sub.That.OPERATORS.set(operators);
|
DemoConfiguration.SUB.That.OPERATORS.set(operators);
|
||||||
System.out.println(" After:");
|
System.out.println(" After:");
|
||||||
DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println);
|
DemoConfiguration.SUB.That.OPERATORS.forEach(System.out::println);
|
||||||
|
|
||||||
System.out.println("> Clear List:");
|
System.out.println("> Clear List:");
|
||||||
System.out.println(" Before: size :" + DemoConfiguration.Sub.That.OPERATORS.size());
|
System.out.println(" Before: size :" + DemoConfiguration.SUB.That.OPERATORS.size());
|
||||||
DemoConfiguration.Sub.That.OPERATORS.modify(List::clear);
|
DemoConfiguration.SUB.That.OPERATORS.modify(List::clear);
|
||||||
System.out.println(" After size :" + DemoConfiguration.Sub.That.OPERATORS.size());
|
System.out.println(" After size :" + DemoConfiguration.SUB.That.OPERATORS.size());
|
||||||
|
|
||||||
System.out.println("> Test Section:");
|
System.out.println("> Test Section:");
|
||||||
System.out.println(DemoConfiguration.MODEL_TEST.get());
|
System.out.println(DemoConfiguration.USERS.get());
|
||||||
DemoConfiguration.MODEL_TEST.set(TestModel.random());
|
DemoConfiguration.USERS.add(UserRecord.random());
|
||||||
|
|
||||||
System.out.println("> Test Maps:");
|
// System.out.println("> Test Maps:");
|
||||||
DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v));
|
// DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v));
|
||||||
LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
|
// LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
|
||||||
for (int i = 1; i <= 5; i++) {
|
// for (int i = 1; i <= 5; i++) {
|
||||||
data.put(i, UUID.randomUUID());
|
// data.put(i, UUID.randomUUID());
|
||||||
}
|
// }
|
||||||
DemoConfiguration.USERS.set(data);
|
// DemoConfiguration.USERS.set(data);
|
||||||
System.out.println("----------------------------------------------------");
|
System.out.println("----------------------------------------------------");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void testInner(ConfigurationProvider<?> provider) {
|
public static void testInner(ConfigurationHolder<?> provider) {
|
||||||
|
|
||||||
TestConfiguration TEST = new TestConfiguration();
|
RegistryConfig TEST = new RegistryConfig();
|
||||||
|
|
||||||
provider.initialize(TEST, true);
|
provider.initialize(TEST);
|
||||||
|
|
||||||
System.out.println("> Test Inner value before:");
|
System.out.println("> Test Inner value before:");
|
||||||
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
|
System.out.println(TEST.INSTANCE.INNER_VALUE.getNotNull());
|
||||||
|
|
||||||
double after = Math.random() * 200D;
|
double after = Math.random() * 200D;
|
||||||
System.out.println("> Test Inner value -> " + after);
|
System.out.println("> Test Inner value -> " + after);
|
||||||
TEST.INNER.INNER_VALUE.set(after);
|
TEST.INSTANCE.INNER_VALUE.set(after);
|
||||||
|
|
||||||
System.out.println("> Test Inner value after:");
|
System.out.println("> Test Inner value after:");
|
||||||
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
|
System.out.println(TEST.INSTANCE.INNER_VALUE.getNotNull());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void save(ConfigurationProvider<?> provider) {
|
public static void save(ConfigurationHolder<?> provider) {
|
||||||
try {
|
try {
|
||||||
provider.save();
|
provider.save();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
+25
-32
@@ -1,17 +1,14 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests.conf;
|
package cc.carm.lib.configuration.demo.tests.conf;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
import cc.carm.lib.configuration.Configuration;
|
||||||
import cc.carm.lib.configuration.core.Configuration;
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
import cc.carm.lib.configuration.annotation.InlineComment;
|
||||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
import cc.carm.lib.configuration.demo.DatabaseConfiguration;
|
||||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
|
||||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
|
||||||
|
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -31,45 +28,41 @@ public interface DemoConfiguration extends Configuration {
|
|||||||
|
|
||||||
// 支持通过 Class<?> 变量标注子配置,一并注册。
|
// 支持通过 Class<?> 变量标注子配置,一并注册。
|
||||||
// 注意: 若对应类也有注解,则优先使用类的注解。
|
// 注意: 若对应类也有注解,则优先使用类的注解。
|
||||||
@ConfigPath("other-class-config") //支持通过注解修改子配置的主路径,若不修改则以变量名自动生成。
|
Class<?> DATABASE = DatabaseConfiguration.class;
|
||||||
@HeaderComment({"", "Something..."}) // 支持给子路径直接打注释
|
|
||||||
@InlineComment("InlineComments for class path")
|
|
||||||
Class<?> OTHER = OtherConfiguration.class;
|
|
||||||
|
|
||||||
@ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
@ConfigPath("registered_users") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||||
@InlineComment("Section数据也支持InlineComment注释")
|
@InlineComment("Section数据也支持InlineComment注释")
|
||||||
ConfigValue<TestModel> MODEL_TEST = ConfiguredSection.builderOf(TestModel.class)
|
ConfiguredList<UserRecord> USERS = ConfiguredList.builderOf(UserRecord.class).fromSection()
|
||||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
.parse(UserRecord::deserialize).serialize(UserRecord::serialize)
|
||||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
.defaults(UserRecord.CARM).build();
|
||||||
.serializeValue(TestModel::serialize).build();
|
|
||||||
|
|
||||||
@HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"})
|
// @HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"})
|
||||||
ConfiguredMap<Integer, UUID> USERS = ConfiguredMap.builderOf(Integer.class, UUID.class)
|
// ConfiguredMap<Integer, UUID> USERS = ConfiguredMap.builderOf(Integer.class, UUID.class)
|
||||||
.asLinkedMap().fromString()
|
// .asLinkedMap().fromString()
|
||||||
.parseKey(Integer::parseInt)
|
// .parseKey(Integer::parseInt)
|
||||||
.parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
|
// .parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
|
||||||
.build();
|
// .build();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支持内部类的直接注册。
|
* 支持内部类的直接注册。
|
||||||
* 注意,需要使用 {@link ConfigInitializer#initialize(Class, boolean, boolean)} 方法,并设定第三个参数为 true。
|
* 注意,需要启用 {@link cc.carm.lib.configuration.source.option.StandardOptions#LOAD_SUB_CLASSES}
|
||||||
*/
|
*/
|
||||||
class Sub extends ConfigurationRoot {
|
class SUB implements Configuration {
|
||||||
|
|
||||||
@ConfigPath(value = "uuid-value", root = true)
|
@ConfigPath(value = "uuid-value", root = true)
|
||||||
@InlineComment("This is an inline comment")
|
@InlineComment("This is an inline comment")
|
||||||
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
|
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
|
||||||
.builderOf(UUID.class).fromString()
|
.builderOf(UUID.class).fromString()
|
||||||
.parseValue((data, defaultValue) -> UUID.fromString(data))
|
.parse((holder, data) -> UUID.fromString(data))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static class That extends ConfigurationRoot {
|
public static class That implements Configuration {
|
||||||
|
|
||||||
public static final ConfiguredList<UUID> OPERATORS = ConfiguredList
|
public static final ConfiguredList<UUID> OPERATORS = ConfiguredList
|
||||||
.builderOf(UUID.class).fromString()
|
.builderOf(UUID.class).fromString()
|
||||||
.parseValue(s -> Objects.requireNonNull(UUID.fromString(s)))
|
.parse(s -> Objects.requireNonNull(UUID.fromString(s)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package cc.carm.lib.configuration.demo.tests.conf;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
|
||||||
|
@HeaderComment("Inner Test")
|
||||||
|
public class InstanceConfig implements Configuration {
|
||||||
|
|
||||||
|
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(1.0D);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests.conf;
|
|
||||||
|
|
||||||
public class OtherConfiguration {
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package cc.carm.lib.configuration.demo.tests.conf;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||||
|
import cc.carm.lib.configuration.annotation.InlineComment;
|
||||||
|
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class RegistryConfig implements Configuration {
|
||||||
|
|
||||||
|
@HeaderComment("Support for configurations as instances")
|
||||||
|
public final InstanceConfig INSTANCE = new InstanceConfig();
|
||||||
|
|
||||||
|
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||||
|
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||||
|
@InlineComment("Section数据也支持InlineComment注释")
|
||||||
|
public final ConfigValue<UserRecord> TEST_MODEL = ConfiguredValue.builderOf(UserRecord.class).fromSection()
|
||||||
|
.defaults(new UserRecord("Carm", UUID.randomUUID()))
|
||||||
|
.parse((holder, section) -> UserRecord.deserialize(section))
|
||||||
|
.serialize((holder, data) -> data.serialize()).build();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests.conf;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
|
||||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
|
||||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
|
||||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
|
||||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class TestConfiguration extends ConfigurationRoot {
|
|
||||||
|
|
||||||
public final TestInnerConfiguration INNER = new TestInnerConfiguration();
|
|
||||||
|
|
||||||
public final ConfigValue<Double> CLASS_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
|
||||||
|
|
||||||
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
|
||||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
|
||||||
@InlineComment("Section数据也支持InlineComment注释")
|
|
||||||
public final ConfigValue<TestModel> TEST_MODEL = ConfiguredSection
|
|
||||||
.builderOf(TestModel.class)
|
|
||||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
|
||||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
|
||||||
.serializeValue(TestModel::serialize).build();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests.conf;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
|
||||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
|
||||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
|
||||||
|
|
||||||
@HeaderComment("Inner Test")
|
|
||||||
public class TestInnerConfiguration extends ConfigurationRoot {
|
|
||||||
|
|
||||||
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
|
||||||
|
|
||||||
}
|
|
||||||
+2
-2
@@ -2,11 +2,11 @@ package cc.carm.lib.configuration.demo.tests.model;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public abstract class AbstractModel {
|
public abstract class AbstractRecord {
|
||||||
|
|
||||||
protected final @NotNull String name;
|
protected final @NotNull String name;
|
||||||
|
|
||||||
public AbstractModel(@NotNull String name) {
|
public AbstractRecord(@NotNull String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
+12
-10
@@ -1,26 +1,28 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests.model;
|
package cc.carm.lib.configuration.demo.tests.model;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class TestModel extends AbstractModel {
|
public class UserRecord extends AbstractRecord {
|
||||||
|
|
||||||
public UUID uuid;
|
public static final UserRecord CARM = new UserRecord("Carm", UUID.fromString("f7b3b3b3-3b3b-3b3b-3b3b-3b3b3b3b3b3b"));
|
||||||
|
|
||||||
public TestModel(String name, UUID uuid) {
|
protected UUID uuid;
|
||||||
|
|
||||||
|
public UserRecord(String name, UUID uuid) {
|
||||||
super(name);
|
super(name);
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUuid(UUID uuid) {
|
public void uuid(UUID uuid) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getUuid() {
|
public UUID uuid() {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,16 +35,16 @@ public class TestModel extends AbstractModel {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TestModel deserialize(ConfigurationWrapper<?> section) {
|
public static UserRecord deserialize(ConfigureSection section) {
|
||||||
String name = section.getString("name");
|
String name = section.getString("name");
|
||||||
if (name == null) throw new NullPointerException("name is null");
|
if (name == null) throw new NullPointerException("name is null");
|
||||||
String uuidString = section.getString("info.uuid");
|
String uuidString = section.getString("info.uuid");
|
||||||
if (uuidString == null) throw new NullPointerException("uuid is null");
|
if (uuidString == null) throw new NullPointerException("uuid is null");
|
||||||
return new TestModel(name, UUID.fromString(uuidString));
|
return new UserRecord(name, UUID.fromString(uuidString));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TestModel random() {
|
public static UserRecord random() {
|
||||||
return new TestModel(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID());
|
return new UserRecord(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>easyconfiguration-parent</artifactId>
|
||||||
|
<version>3.9.1</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<artifactId>easyconfiguration-feature-commentable</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package cc.carm.lib.configuration.annotation;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Footer Comments.
|
||||||
|
* Add a comment to the bottom of the corresponding configuration for easy reading and viewing.
|
||||||
|
* <p>e.g.
|
||||||
|
* <blockquote><pre>
|
||||||
|
* foo: "bar"
|
||||||
|
* # The first line of the comment
|
||||||
|
* # The second line of the comment
|
||||||
|
* </pre></blockquote>
|
||||||
|
*/
|
||||||
|
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface FooterComment {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the content of the note is 0, it will be treated as a blank line.
|
||||||
|
* <p> e.g. <b>{"foo","","bar"}</b>
|
||||||
|
* Will be set as
|
||||||
|
* <blockquote><pre>
|
||||||
|
* foo: "bar"
|
||||||
|
* # foo
|
||||||
|
*
|
||||||
|
* # bar
|
||||||
|
* </pre></blockquote>
|
||||||
|
*
|
||||||
|
* @return The content of this comment
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
String[] value() default "";
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.core.annotation;
|
package cc.carm.lib.configuration.annotation;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
+20
-1
@@ -1,4 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.core.annotation;
|
package cc.carm.lib.configuration.annotation;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@@ -31,4 +31,23 @@ public @interface InlineComment {
|
|||||||
@NotNull
|
@NotNull
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the regex is not empty, the comment will be added to
|
||||||
|
* all sub paths if the regex matches the value.
|
||||||
|
* If the regex is empty, the comment will be added to the current path.
|
||||||
|
* <p> e.g. <b>"^foo\\.*\\.bar"</b> will be set like
|
||||||
|
* <blockquote><pre>
|
||||||
|
* foo:
|
||||||
|
* some:
|
||||||
|
* lover: "bar" <- not matched so no comments
|
||||||
|
* bar: "foobar" # Comment Contents
|
||||||
|
* other:
|
||||||
|
* bar: "foobar" # Comment Contents
|
||||||
|
* </pre></blockquote>
|
||||||
|
*
|
||||||
|
* @return The path regexes of this comment
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
String[] regex() default {};
|
||||||
|
|
||||||
}
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package cc.carm.lib.configuration.commentable;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.annotation.FooterComment;
|
||||||
|
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||||
|
import cc.carm.lib.configuration.annotation.InlineComment;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CommentableMetaTypes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration's {@link HeaderComment}
|
||||||
|
*/
|
||||||
|
ConfigurationMetadata<List<String>> HEADER_COMMENTS = ConfigurationMetadata.of(Collections.emptyList());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration's footer comments
|
||||||
|
*/
|
||||||
|
ConfigurationMetadata<List<String>> FOOTER_COMMENTS = ConfigurationMetadata.of(Collections.emptyList());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration's {@link InlineComment}
|
||||||
|
*/
|
||||||
|
ConfigurationMetadata<String> INLINE_COMMENT = ConfigurationMetadata.of();
|
||||||
|
|
||||||
|
|
||||||
|
static void register(@NotNull ConfigurationHolder<?> provider) {
|
||||||
|
register(provider.initializer());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register(@NotNull ConfigurationInitializer initializer) {
|
||||||
|
initializer.registerAnnotation(
|
||||||
|
HeaderComment.class, HEADER_COMMENTS,
|
||||||
|
a -> Arrays.asList(a.value())
|
||||||
|
);
|
||||||
|
initializer.registerAnnotation(
|
||||||
|
FooterComment.class, FOOTER_COMMENTS,
|
||||||
|
a -> Arrays.asList(a.value())
|
||||||
|
);
|
||||||
|
initializer.registerAnnotation(InlineComment.class, INLINE_COMMENT, InlineComment::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package cc.carm.lib.configuration.option;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||||
|
|
||||||
|
public interface CommentableOptions {
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Whether to keep modified comments in configuration,
|
||||||
|
// * that means we only set comments for values that are not exists in configuration.
|
||||||
|
// */
|
||||||
|
// ConfigurationOption<Boolean> KEEP_COMMENTS = ConfigurationOption.of(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to comment values name that are not exists in configuration and no default value offered.
|
||||||
|
* <br>If true, a value without default value is not exists in configuration, we will comment its name,
|
||||||
|
* <p>e.g. a value named "foo" without default value will be put as:
|
||||||
|
* <blockquote><pre>
|
||||||
|
* # Value comments
|
||||||
|
* # foo:
|
||||||
|
* </pre></blockquote>
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> COMMENT_EMPTY_VALUE = ConfigurationOption.of(true);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>easyconfiguration-parent</artifactId>
|
||||||
|
<version>3.9.1</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<artifactId>easyconfiguration-feature-file</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package cc.carm.lib.configuration.source.file;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationFactory;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public abstract class FileConfigFactory<SOURCE extends FileConfigSource<?, ?, SOURCE>,
|
||||||
|
HOLDER extends ConfigurationHolder<SOURCE>, SELF extends FileConfigFactory<SOURCE, HOLDER, SELF>>
|
||||||
|
extends ConfigurationFactory<SOURCE, HOLDER, SELF> {
|
||||||
|
|
||||||
|
|
||||||
|
protected @NotNull File file;
|
||||||
|
protected @Nullable String resourcePath;
|
||||||
|
|
||||||
|
public FileConfigFactory(@NotNull File file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF file(@NotNull File file) {
|
||||||
|
this.file = file;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF file(@NotNull Path path) {
|
||||||
|
return file(path.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF file(@NotNull File parent, @NotNull String fileName) {
|
||||||
|
return file(new File(parent, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF resourcePath(@Nullable String resourcePath) {
|
||||||
|
this.resourcePath = resourcePath;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
package cc.carm.lib.configuration.source.file;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.option.FileConfigOptions;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public abstract class FileConfigSource<SECTION extends ConfigureSection, ORIGINAL, SELF extends FileConfigSource<SECTION, ORIGINAL, SELF>>
|
||||||
|
extends ConfigureSource<SECTION, ORIGINAL, SELF> {
|
||||||
|
|
||||||
|
protected final @NotNull File file;
|
||||||
|
protected final @Nullable String resourcePath;
|
||||||
|
|
||||||
|
protected FileConfigSource(@NotNull ConfigurationHolder<? extends SELF> holder, long lastUpdateMillis,
|
||||||
|
@NotNull File file, @Nullable String resourcePath) {
|
||||||
|
super(holder, lastUpdateMillis);
|
||||||
|
this.file = file;
|
||||||
|
this.resourcePath = resourcePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Charset charset() {
|
||||||
|
return holder().options().get(FileConfigOptions.CHARSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean copyDefaults() {
|
||||||
|
return holder().options().get(FileConfigOptions.COPY_DEFAULTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void initializeFile() throws IOException {
|
||||||
|
if (this.file.exists()) return;
|
||||||
|
|
||||||
|
File parent = this.file.getParentFile();
|
||||||
|
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
||||||
|
throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourcePath != null && copyDefaults()) {
|
||||||
|
try {
|
||||||
|
saveResource(resourcePath, false);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.file.exists() && !this.file.createNewFile()) {
|
||||||
|
throw new IOException("Failed to create file " + file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <R> R fileInputStream(@NotNull DataFunction<InputStream, R> loader) throws Exception {
|
||||||
|
try (InputStream is = Files.newInputStream(file.toPath())) {
|
||||||
|
return loader.handle(is);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <R> R fileReader(@NotNull DataFunction<Reader, R> loader) throws Exception {
|
||||||
|
try (InputStream is = Files.newInputStream(file.toPath())) {
|
||||||
|
try (Reader r = new InputStreamReader(is, charset())) {
|
||||||
|
return loader.handle(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fileOutputStream(@NotNull Consumer<OutputStream> stream) throws Exception {
|
||||||
|
try (OutputStream os = Files.newOutputStream(file.toPath())) {
|
||||||
|
stream.accept(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fileWriter(@NotNull Consumer<Writer> writer) throws Exception {
|
||||||
|
try (OutputStream os = Files.newOutputStream(file.toPath())) {
|
||||||
|
try (Writer w = new OutputStreamWriter(os, charset())) {
|
||||||
|
writer.accept(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveResource(@NotNull String resourcePath, boolean replace)
|
||||||
|
throws IOException, IllegalArgumentException {
|
||||||
|
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null");
|
||||||
|
if (resourcePath.isEmpty()) throw new IllegalArgumentException("ResourcePath cannot be empty");
|
||||||
|
|
||||||
|
resourcePath = resourcePath.replace('\\', '/');
|
||||||
|
|
||||||
|
URL url = this.getClass().getClassLoader().getResource(resourcePath);
|
||||||
|
if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
|
||||||
|
|
||||||
|
File outDir = file.getParentFile();
|
||||||
|
|
||||||
|
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
|
||||||
|
if (!file.exists() || replace) {
|
||||||
|
try (OutputStream out = Files.newOutputStream(file.toPath())) {
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
try (InputStream in = connection.getInputStream()) {
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(buf)) > 0) {
|
||||||
|
out.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected InputStream getResource(@NotNull String filename) {
|
||||||
|
try {
|
||||||
|
URL url = this.getClass().getClassLoader().getResource(filename);
|
||||||
|
if (url == null) return null;
|
||||||
|
URLConnection connection = url.openConnection();
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
return connection.getInputStream();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package cc.carm.lib.configuration.source.option;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
public interface FileConfigOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The charset of the file.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Charset> CHARSET = ConfigurationOption.of(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to copy files from resource if exists.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> COPY_DEFAULTS = ConfigurationOption.of(true);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>easyconfiguration-parent</artifactId>
|
||||||
|
<version>3.9.1</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<artifactId>easyconfiguration-feature-versioned</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package cc.carm.lib.configuration.annotation;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Range;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of specific {@link cc.carm.lib.configuration.value.ConfigValue}.
|
||||||
|
* <br> Used for versioning target field for rewrite/upgrade.
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface ConfigVersion {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of the configuration field.
|
||||||
|
*
|
||||||
|
* @return the version of the configuration field
|
||||||
|
*/
|
||||||
|
@Range(from = 0, to = Integer.MAX_VALUE)
|
||||||
|
int value() default 0;
|
||||||
|
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package cc.carm.lib.configuration.commentable;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.annotation.ConfigVersion;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public interface VersionedMetaTypes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The version of specific {@link cc.carm.lib.configuration.value.ConfigValue}.
|
||||||
|
* <br> Used for versioning target field for rewrite/upgrade.
|
||||||
|
*/
|
||||||
|
ConfigurationMetadata<Integer> VERSION = ConfigurationMetadata.of(0);
|
||||||
|
|
||||||
|
static void register(@NotNull ConfigurationHolder<?> provider) {
|
||||||
|
register(provider.initializer());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register(@NotNull ConfigurationInitializer initializer) {
|
||||||
|
initializer.registerAnnotation(ConfigVersion.class, VERSION, ConfigVersion::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package cc.carm.lib.configuration.option;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||||
|
|
||||||
|
public interface VersionedOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to set newer defaults when a {@link cc.carm.lib.configuration.value.ConfigValue}'s marked version
|
||||||
|
* is newer than the current in storage.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> RESET_NEWER_DEFAULTS = ConfigurationOption.of(true);
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user