1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2026-06-04 10:38:19 +08:00

feat(exception): Supported ConfigExceptionHandler for holders.

This commit is contained in:
2025-03-17 01:06:57 +08:00
parent 65f3cc1b3d
commit 82cca5eca2
18 changed files with 190 additions and 131 deletions
@@ -1,21 +0,0 @@
package cc.carm.lib.configuration.annotation;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
public class Ex {
static void init(ConfigurationInitializer initializer) {
initializer.appendFieldInitializer((holder, path, field, value) -> {
ValueRange range = field.getAnnotation(ValueRange.class);
if (range == null) return;
value.validate((h, v) -> {
if (!(v instanceof Number)) return;
Number number = (Number) v;
if (number.doubleValue() >= range.min() && number.doubleValue() <= range.max()) {
throw new IllegalArgumentException(range.message());
}
});
});
}
}
@@ -29,7 +29,7 @@ public abstract class AbstractConfigBuilder<
protected @Nullable HOLDER holder;
protected @Nullable String path;
protected @NotNull ValueValidator<? super UNIT> valueValidator = ValueValidator.none();
protected @NotNull ValueValidator<UNIT> valueValidator = ValueValidator.none();
protected @NotNull Supplier<@Nullable TYPE> defaultValueSupplier = () -> null;
protected @NotNull BiConsumer<ConfigurationHolder<?>, String> initializer = (h, p) -> {
};
@@ -63,7 +63,7 @@ public abstract class AbstractConfigBuilder<
* @param validator The validator to set.
* @return this builder
*/
public SELF validator(@NotNull ValueValidator<? super UNIT> validator) {
public SELF validator(@NotNull ValueValidator<UNIT> validator) {
this.valueValidator = validator;
return self();
}
@@ -81,20 +81,17 @@ public abstract class AbstractConfigBuilder<
/**
* Validate the value with the specified condition.
*
* @param validator The validator to set.
* @param validator The validator to append.
* @return this builder
*/
public SELF validate(@NotNull ValueValidator<? super UNIT> validator) {
return validator((h, v) -> {
this.valueValidator.validate(h, v);
validator.validate(h, v);
});
return validator(this.valueValidator.and(validator));
}
/**
* Validate the value with the specified condition.
*
* @param validator The validator to set.
* @param validator The validator to append.
* @return this builder
*/
public SELF validate(@NotNull DataValidator<? super UNIT> validator) {
@@ -0,0 +1,22 @@
package cc.carm.lib.configuration.function;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface ConfigExceptionHandler {
void handle(@NotNull String path, @NotNull Throwable throwable);
static @NotNull ConfigExceptionHandler silence() {
return (path, throwable) -> {
};
}
static @NotNull ConfigExceptionHandler print() {
return (path, throwable) -> {
System.err.println("Error occurred at path: " + path);
throwable.printStackTrace();
};
}
}
@@ -9,7 +9,7 @@ public interface ValueValidator<T> {
void validate(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception;
default ValueValidator<T> compose(ValueValidator<? super T> other) {
default ValueValidator<T> and(ValueValidator<? super T> other) {
return (holder, value) -> {
validate(holder, value);
other.validate(holder, value);
@@ -2,7 +2,9 @@ package cc.carm.lib.configuration.source;
import cc.carm.lib.configuration.adapter.*;
import cc.carm.lib.configuration.adapter.strandard.StandardAdapters;
import cc.carm.lib.configuration.function.ConfigExceptionHandler;
import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.function.ValueValidator;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.loader.PathGenerator;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
@@ -33,10 +35,11 @@ public abstract class ConfigurationFactory<
SELF
> {
protected ValueAdapterRegistry adapters = new ValueAdapterRegistry();
protected ConfigurationOptionHolder options = new ConfigurationOptionHolder();
protected @NotNull ValueAdapterRegistry adapters = new ValueAdapterRegistry();
protected @NotNull ConfigurationOptionHolder options = new ConfigurationOptionHolder();
protected @NotNull Map<String, ConfigurationMetaHolder> metadata = new HashMap<>();
protected ConfigurationInitializer initializer = new ConfigurationInitializer();
protected @NotNull ConfigurationInitializer initializer = new ConfigurationInitializer();
protected @NotNull ConfigExceptionHandler exceptionHandler = ConfigExceptionHandler.print();
protected ConfigurationFactory() {
this.adapters.register(StandardAdapters.PRIMITIVES);
@@ -147,6 +150,11 @@ public abstract class ConfigurationFactory<
return self();
}
public SELF exceptionally(@NotNull ConfigExceptionHandler handler) {
this.exceptionHandler = handler;
return self();
}
/**
* Supply the base path generator for this configuration holder
*
@@ -154,9 +162,7 @@ public abstract class ConfigurationFactory<
* @return this
*/
public SELF pathGenerator(PathGenerator generator) {
return initializer(loader -> {
loader.pathGenerator(generator);
});
return initializer(loader -> loader.pathGenerator(generator));
}
/**
@@ -175,6 +181,19 @@ public abstract class ConfigurationFactory<
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
}
/**
* Register a new annotation for {@link ValueValidator} to the configuration loader
*
* @param annotation The {@link Annotation}
* @param builder The {@link Function} to build the {@link ValueValidator} from the annotation
* @param <A> The annotation type
* @return this
*/
public <A extends Annotation> SELF validAnnotation(@NotNull Class<A> annotation,
@NotNull Function<A, ValueValidator<Object>> builder) {
return initializer(loader -> loader.registerValidAnnotation(annotation, builder));
}
/**
* Build the configuration holder.
*
@@ -3,6 +3,7 @@ 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.function.ConfigExceptionHandler;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
@@ -31,14 +32,25 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
protected final @NotNull ConfigurationInitializer initializer;
protected @NotNull ConfigExceptionHandler exceptionHandler;
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
@NotNull ConfigurationOptionHolder options,
@NotNull Map<String, ConfigurationMetaHolder> metadata,
@NotNull ConfigurationInitializer initializer) {
this(adapters, options, metadata, initializer, ConfigExceptionHandler.print());
}
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
@NotNull ConfigurationOptionHolder options,
@NotNull Map<String, ConfigurationMetaHolder> metadata,
@NotNull ConfigurationInitializer initializer,
@NotNull ConfigExceptionHandler exceptionHandler) {
this.initializer = initializer;
this.adapters = adapters;
this.options = options;
this.metadata = metadata;
this.exceptionHandler = exceptionHandler;
}
public abstract @NotNull SOURCE config();
@@ -117,7 +129,7 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
try {
initializer.initialize(this, configClass);
} catch (Exception e) {
e.printStackTrace();
throwing(configClass.getName(), e);
}
}
@@ -125,7 +137,7 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
try {
initializer.initialize(this, config);
} catch (Exception e) {
e.printStackTrace();
throwing(config.getClass().getName(), e);
}
}
@@ -133,4 +145,12 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
value.holder(this);
}
public void throwing(@NotNull String path, @NotNull Throwable e) {
this.exceptionHandler.handle(path, e);
}
public void exceptionally(@NotNull ConfigExceptionHandler handler) {
this.exceptionHandler = handler;
}
}
@@ -134,7 +134,7 @@ public class ConfigurationInitializer {
try {
this.classInitializer.whenInitialize(holder, path, root.getClass(), root);
} catch (Exception e) {
e.printStackTrace();
holder.throwing(path, e);
}
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path));
}
@@ -151,7 +151,7 @@ public class ConfigurationInitializer {
try {
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz, configField);
} catch (Exception e) {
e.printStackTrace();
holder.throwing(path, e);
}
for (Field field : clazz.getDeclaredFields()) {
@@ -185,7 +185,7 @@ public class ConfigurationInitializer {
try {
this.valueInitializer.whenInitialize(holder, path, field, value);
} catch (Exception e) {
e.printStackTrace();
holder.throwing(path, e);
}
if (holder.option(StandardOptions.PRELOAD)) {
value.get(); // Preload the value by calling #get method.
@@ -20,7 +20,7 @@ public class ValueManifest<TYPE, UNIT> {
protected @Nullable ConfigurationHolder<?> holder;
protected @Nullable String path; // Section path
protected @NotNull ValueValidator<? super UNIT> validator;
protected @NotNull ValueValidator<UNIT> validator;
protected @NotNull Supplier<@Nullable TYPE> defaultSupplier;
public ValueManifest(@NotNull ValueType<TYPE> type) {
@@ -37,19 +37,19 @@ public class ValueManifest<TYPE, UNIT> {
public ValueManifest(@NotNull ValueType<TYPE> type,
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
@NotNull ValueValidator<? super UNIT> validator) {
@NotNull ValueValidator<UNIT> validator) {
this(type, defaultSupplier, validator, EMPTY_INITIALIZER, null, null);
}
public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier,
@NotNull ValueValidator<? super UNIT> validator,
@NotNull ValueValidator<UNIT> validator,
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer) {
this(type, defaultSupplier, validator, initializer, null, null);
}
public ValueManifest(@NotNull ValueType<TYPE> type,
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
@NotNull ValueValidator<? super UNIT> validator,
@NotNull ValueValidator<UNIT> validator,
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer,
@Nullable ConfigurationHolder<?> holder, @Nullable String path) {
this.type = type;
@@ -103,19 +103,16 @@ public class ValueManifest<TYPE, UNIT> {
return defaults() != null;
}
public @NotNull ValueValidator<? super UNIT> validator() {
public @NotNull ValueValidator<UNIT> validator() {
return this.validator;
}
public void validator(@NotNull ValueValidator<? super UNIT> validator) {
public void validator(@NotNull ValueValidator<UNIT> validator) {
this.validator = validator;
}
public void validate(@NotNull ValueValidator<? super UNIT> validator) {
validator((h, v) -> {
this.validator.validate(h, v);
validator.validate(h, v);
});
public void validate(@NotNull ValueValidator<UNIT> validator) {
validator(this.validator.and(validator));
}
protected UNIT withValidated(@Nullable UNIT value) throws Exception {
@@ -151,6 +148,15 @@ public class ValueManifest<TYPE, UNIT> {
config().set(path(), value);
}
protected void throwing(@NotNull Throwable throwable) {
throwing(path, throwable);
}
protected void throwing(@NotNull String path, @NotNull Throwable throwable) {
if (holder == null) throwable.printStackTrace();
else holder.throwing(path, throwable);
}
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, valuePath) -> {
};
@@ -87,20 +87,25 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>, V> implements
if (!cacheExpired()) return getCachedOrDefault(createList());
// Data that is outdated and needs to be parsed again.
List<V> list = createList();
try {
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);
int i = 0;
for (Object dataVal : data) {
if (dataVal == null) continue;
try {
list.add(withValidated(parser.parse(holder(), paramType(), dataVal)));
} catch (Exception e) {
e.printStackTrace();
throwing(path + "[" + i + "]", e);
}
}
} catch (Exception ex) {
throwing(ex);
}
return updateCache(list);
}
@@ -121,7 +126,7 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>, V> implements
try {
data.add(serializer.serialize(holder(), paramType(), withValidated(val)));
} catch (Exception ex) {
ex.printStackTrace();
throwing(ex);
}
}
setData(data);
@@ -77,6 +77,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>, V> impleme
// If the value is expired, we need to update it
Map<K, V> map = createMap();
try {
ConfigureSection section = config().getSection(path());
if (section == null) return getDefaultFirst(map);
@@ -96,9 +97,12 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>, V> impleme
V value = valueParser.parse(holder(), valueType(), dataVal);
map.put(key, withValidated(value));
} catch (Exception e) {
e.printStackTrace();
throwing(path + "." + dataKey, e);
}
}
} catch (Exception ex) {
throwing(ex);
}
return updateCache(map);
}
@@ -120,6 +124,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>, V> impleme
return;
}
try {
ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
if (keySerializer == null) return;
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
@@ -134,10 +139,13 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>, V> impleme
valueSerializer.serialize(holder(), valueType(), withValidated(entry.getValue()))
);
} catch (Exception e) {
e.printStackTrace();
throwing(path + "." + entry.getKey(), e);
}
}
setData(data);
} catch (Exception ex) {
throwing(ex);
}
}
public <T> @NotNull T handle(Function<Map<K, V>, T> function) {
@@ -137,20 +137,19 @@ public class ConfiguredValue<V> extends CachedConfigValue<V, V> {
if (!cacheExpired()) return getCachedOrDefault();
// Data that is outdated and needs to be parsed again.
try {
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.
V parsed = parser.parse(holder(), type(), data);
return updateCache(withValidated(parsed));
} catch (Exception e) {
// There was a validate or parsing error, prompted and returned the default value.
e.printStackTrace();
throwing(e);
return defaults();
}
@@ -170,13 +169,13 @@ public class ConfiguredValue<V> extends CachedConfigValue<V, V> {
return;
}
try {
ValueSerializer<V> serializer = serializer();
if (serializer == null) return; // No serializer, do nothing.
try {
setData(serializer.serialize(holder(), type(), withValidated(value)));
} catch (Exception e) {
e.printStackTrace();
throwing(e);
}
}
+8
View File
@@ -13,6 +13,7 @@
<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>configured-demo</artifactId>
<packaging>jar</packaging>
@@ -40,6 +41,13 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-validators</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@@ -3,7 +3,6 @@ package cc.carm.lib.configuration.demo;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@HeaderComments({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
@@ -16,17 +15,17 @@ public interface DatabaseConfiguration extends Configuration {
"- MySQL(新): com.mysql.cj.jdbc.Driver",
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
})
ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
ConfiguredValue<String> DRIVER_NAME = ConfiguredValue.of(
String.class, "com.mysql.cj.jdbc.Driver"
);
ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
ConfiguredValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
ConfiguredValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
ConfiguredValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
ConfiguredValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
ConfiguredValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
static String buildJDBC() {
return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get());
@@ -29,10 +29,7 @@ public interface DemoConfiguration extends Configuration {
@ConfigPath(root = true)
@FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"})
ConfiguredValue<Long> TEST_NUMBER = ConfiguredValue.with(Long.class)
.validate(l -> l > 100, "数值必须大于100")
.validate(l -> l < 100000000, "数值必须小于100000000")
.defaults(123456789L).build();
ConfiguredValue<Long> TEST_NUMBER = ConfiguredValue.with(Long.class).defaults(123456789L).build();
@HeaderComments({"枚举类型测试"})
@FooterComments({"上述的枚举内容本质上是通过STRING解析的"})
@@ -2,11 +2,13 @@ package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.annotation.ValueRange;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@HeaderComments("Inner Test")
@HeaderComments("service")
public class InstanceConfig implements Configuration {
@ValueRange(min = 0, max = 100, message = "The value must be between 0 and 100")
public final ConfiguredValue<Double> STATUS = ConfiguredValue.of(1.0D);
}
@@ -11,7 +11,11 @@ import java.util.regex.Pattern;
public class Validators {
public static void initialize(ConfigurationHolder<?> holder) {
private Validators() {
throw new UnsupportedOperationException("API Register.");
}
public static void activate(ConfigurationHolder<?> holder) {
holder.initializer().registerValidAnnotation(ValueRange.class, r -> (ho, value) -> {
if (!(value instanceof Number)) {
throw new IllegalArgumentException("Value is not a number: " + value);
@@ -1,10 +1,8 @@
package config;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import org.junit.Test;
import java.io.File;
@@ -18,12 +16,6 @@ public class JSONConfigTest {
@Test
public void onTest() {
ConfigValue<Boolean> EXAMPLE = ConfiguredValue.of(false);
EXAMPLE.initialize(this.holder, "example");
System.out.println("Example: " + EXAMPLE.get());
ConfigurationTest.testDemo(this.holder);
ConfigurationTest.testInner(this.holder);
@@ -1,11 +1,11 @@
package yaml.test;
import cc.carm.lib.configuration.commentable.Commentable;
import cc.carm.lib.configuration.commentable.CommentableMeta;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory;
import cc.carm.lib.configuration.source.yaml.YAMLSource;
import cc.carm.lib.configuration.validators.Validators;
import org.junit.Test;
import java.util.List;
@@ -19,6 +19,8 @@ public class YamlTests {
ConfigurationHolder<YAMLSource> holder = YAMLConfigFactory.from("target/tests.yml")
.resourcePath("configs/sample.yml").build();
Validators.activate(holder);
ConfigurationTest.testDemo(holder);
ConfigurationTest.testInner(holder);