Refactored validation and stuff
This commit is contained in:
@@ -2,11 +2,9 @@ package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.validation.ConfigEntryValueValidationException;
|
||||
|
||||
public interface ConfigEntry<T> {
|
||||
Class<T> valueClass();
|
||||
void validate(T value) throws ConfigEntryValueValidationException;
|
||||
|
||||
void seal(ConfigContainer<?> container);
|
||||
boolean sealed();
|
||||
@@ -14,4 +12,5 @@ public interface ConfigEntry<T> {
|
||||
EntryExtensionsData extensionsData();
|
||||
|
||||
void visitInOrder(ConfigEntryVisitor visitor);
|
||||
void visitInOrder(ConfigEntryValueVisitor visitor, T value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
public interface ConfigEntryValueVisitor {
|
||||
<T> void visitEntry(ConfigEntry<T> entry, T value);
|
||||
|
||||
default <T> boolean enterCollectionEntry(ConfigEntry<T> entry, T value) {
|
||||
visitEntry(entry, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
default <T> void leaveCollectionEntry(ConfigEntry<T> entry, T value) {
|
||||
}
|
||||
|
||||
default <T> boolean enterCompoundEntry(ConfigEntry<T> entry, T value) {
|
||||
visitEntry(entry, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean enterCompoundSubEntry(String key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default void leaveCompoundSubEntry(String key) {
|
||||
}
|
||||
|
||||
default <T> void leaveCompoundEntry(ConfigEntry<T> entry, T value) {
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,5 @@ import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
public interface TweedExtensionSetupContext {
|
||||
ConfigContainer<?> configContainer();
|
||||
<E> RegisteredExtensionData<EntryExtensionsData, E> registerEntryExtensionData(Class<E> dataClass);
|
||||
void registerExtension(TweedExtension extension);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.siphalor.tweed5.core.api.middleware;
|
||||
|
||||
import de.siphalor.tweed5.core.api.sort.AcyclicGraphSorter;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
@@ -10,6 +11,7 @@ import java.util.stream.Stream;
|
||||
public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
private static final String CONTAINER_ID = "";
|
||||
|
||||
@Getter
|
||||
private List<Middleware<M>> middlewares = new ArrayList<>();
|
||||
private final Set<String> middlewareIds = new HashSet<>();
|
||||
private boolean sealed = false;
|
||||
@@ -20,19 +22,37 @@ public class DefaultMiddlewareContainer<M> implements MiddlewareContainer<M> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Middleware<M> middleware) {
|
||||
if (sealed) {
|
||||
throw new IllegalStateException("Middleware container has already been sealed");
|
||||
public void registerAll(Collection<Middleware<M>> middlewares) {
|
||||
requireUnsealed();
|
||||
|
||||
for (Middleware<M> middleware : middlewares) {
|
||||
if (middleware.id().isEmpty()) {
|
||||
throw new IllegalArgumentException("Middleware id cannot be empty");
|
||||
}
|
||||
if (!this.middlewareIds.add(middleware.id())) {
|
||||
throw new IllegalArgumentException("Middleware id already registered: " + middleware.id());
|
||||
}
|
||||
}
|
||||
this.middlewares.addAll(middlewares);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Middleware<M> middleware) {
|
||||
requireUnsealed();
|
||||
|
||||
if (middleware.id().isEmpty()) {
|
||||
throw new IllegalArgumentException("Middleware id cannot be empty");
|
||||
}
|
||||
if (middlewareIds.contains(middleware.id())) {
|
||||
if (!middlewareIds.add(middleware.id())) {
|
||||
throw new IllegalArgumentException("Middleware id already registered: " + middleware.id());
|
||||
}
|
||||
|
||||
middlewares.add(middleware);
|
||||
middlewareIds.add(middleware.id());
|
||||
}
|
||||
|
||||
private void requireUnsealed() {
|
||||
if (sealed) {
|
||||
throw new IllegalStateException("Middleware container has already been sealed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package de.siphalor.tweed5.core.api.middleware;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface MiddlewareContainer<M> extends Middleware<M> {
|
||||
default void registerAll(Collection<Middleware<M>> middlewares) {
|
||||
middlewares.forEach(this::register);
|
||||
}
|
||||
void register(Middleware<M> middleware);
|
||||
void seal();
|
||||
Collection<Middleware<M>> middlewares();
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package de.siphalor.tweed5.core.api.validation;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
|
||||
public interface ConfigEntryValidationExtension {
|
||||
Middleware<ConfigEntryValidationMiddleware> validationMiddleware(ConfigEntry<?> configEntry);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package de.siphalor.tweed5.core.api.validation;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigEntryValidationMiddleware {
|
||||
<T> void validate(ConfigEntry<T> configEntry, T value) throws ConfigEntryValueValidationException;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package de.siphalor.tweed5.core.api.validation;
|
||||
|
||||
public class ConfigEntryValueValidationException extends Exception {
|
||||
public ConfigEntryValueValidationException() {
|
||||
}
|
||||
|
||||
public ConfigEntryValueValidationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ConfigEntryValueValidationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConfigEntryValueValidationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -20,7 +21,7 @@ import java.util.Map;
|
||||
public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
@Getter
|
||||
private ConfigContainerSetupPhase setupPhase = ConfigContainerSetupPhase.EXTENSIONS_SETUP;
|
||||
private final HashMap<Class<? extends TweedExtension>, TweedExtension> extensions = new HashMap<>();
|
||||
private final Map<Class<? extends TweedExtension>, TweedExtension> extensions = new HashMap<>();
|
||||
private ConfigEntry<T> rootEntry;
|
||||
private PatchworkClass<EntryExtensionsData> entryExtensionsDataPatchworkClass;
|
||||
private Map<Class<?>, RegisteredExtensionDataImpl<EntryExtensionsData, ?>> registeredEntryDataExtensions;
|
||||
@@ -33,7 +34,11 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
@Override
|
||||
public void registerExtension(TweedExtension extension) {
|
||||
requireSetupPhase(ConfigContainerSetupPhase.EXTENSIONS_SETUP);
|
||||
extensions.put(extension.getClass(), extension);
|
||||
|
||||
TweedExtension previous = extensions.put(extension.getClass(), extension);
|
||||
if (previous != null) {
|
||||
throw new IllegalArgumentException("Extension " + extension.getClass().getName() + " is already registered");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -41,6 +46,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
requireSetupPhase(ConfigContainerSetupPhase.EXTENSIONS_SETUP);
|
||||
registeredEntryDataExtensions = new HashMap<>();
|
||||
|
||||
Collection<TweedExtension> additionalExtensions = new ArrayList<>();
|
||||
TweedExtensionSetupContext extensionSetupContext = new TweedExtensionSetupContext() {
|
||||
@Override
|
||||
public ConfigContainer<T> configContainer() {
|
||||
@@ -56,10 +62,26 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
registeredEntryDataExtensions.put(dataClass, registered);
|
||||
return registered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerExtension(TweedExtension extension) {
|
||||
if (!extensions.containsKey(extension.getClass())) {
|
||||
additionalExtensions.add(extension);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (TweedExtension extension : extensions.values()) {
|
||||
extension.setup(extensionSetupContext);
|
||||
Collection<TweedExtension> extensionsToSetup = extensions.values();
|
||||
while (!extensionsToSetup.isEmpty()) {
|
||||
for (TweedExtension extension : extensionsToSetup) {
|
||||
extension.setup(extensionSetupContext);
|
||||
}
|
||||
|
||||
for (TweedExtension additionalExtension : additionalExtensions) {
|
||||
extensions.put(additionalExtension.getClass(), additionalExtension);
|
||||
}
|
||||
extensionsToSetup = new ArrayList<>(additionalExtensions);
|
||||
additionalExtensions.clear();
|
||||
}
|
||||
|
||||
PatchworkClassCreator<EntryExtensionsData> entryExtensionsDataGenerator = PatchworkClassCreator.<EntryExtensionsData>builder()
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
||||
import de.siphalor.tweed5.core.api.middleware.MiddlewareContainer;
|
||||
import de.siphalor.tweed5.core.api.validation.ConfigEntryValidationExtension;
|
||||
import de.siphalor.tweed5.core.api.validation.ConfigEntryValidationMiddleware;
|
||||
import de.siphalor.tweed5.core.api.validation.ConfigEntryValueValidationException;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -17,17 +10,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
abstract class BaseConfigEntryImpl<T> implements ConfigEntry<T> {
|
||||
private static final ConfigEntryValidationMiddleware ROOT_VALIDATION = new ConfigEntryValidationMiddleware() {
|
||||
@Override
|
||||
public <U> void validate(ConfigEntry<U> configEntry, U value) {}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
private final Class<T> valueClass;
|
||||
private ConfigContainer<?> container;
|
||||
private EntryExtensionsData extensionsData;
|
||||
private boolean sealed;
|
||||
private ConfigEntryValidationMiddleware validationMiddleware;
|
||||
|
||||
@Override
|
||||
public void seal(ConfigContainer<?> container) {
|
||||
@@ -35,18 +23,6 @@ abstract class BaseConfigEntryImpl<T> implements ConfigEntry<T> {
|
||||
|
||||
this.container = container;
|
||||
this.extensionsData = container.createExtensionsData();
|
||||
|
||||
MiddlewareContainer<ConfigEntryValidationMiddleware> validationMiddlewareContainer = new DefaultMiddlewareContainer<>();
|
||||
|
||||
for (TweedExtension extension : container().extensions()) {
|
||||
if (extension instanceof ConfigEntryValidationExtension) {
|
||||
validationMiddlewareContainer.register(((ConfigEntryValidationExtension) extension).validationMiddleware(this));
|
||||
}
|
||||
}
|
||||
validationMiddlewareContainer.seal();
|
||||
|
||||
validationMiddleware = validationMiddlewareContainer.process(ROOT_VALIDATION);
|
||||
|
||||
sealed = true;
|
||||
}
|
||||
|
||||
@@ -55,22 +31,4 @@ abstract class BaseConfigEntryImpl<T> implements ConfigEntry<T> {
|
||||
throw new IllegalStateException("Config entry is already sealed!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(T value) throws ConfigEntryValueValidationException {
|
||||
if (value == null) {
|
||||
if (valueClass.isPrimitive()) {
|
||||
throw new ConfigEntryValueValidationException("Value must not be null");
|
||||
}
|
||||
} else if (!valueClass.isAssignableFrom(value.getClass())) {
|
||||
throw new ConfigEntryValueValidationException("Value must be of type " + valueClass.getName());
|
||||
}
|
||||
|
||||
validationMiddleware.validate(this, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryVisitor visitor) {
|
||||
visitor.visitEntry(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.CoherentCollectionConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -39,4 +40,16 @@ public class CoherentCollectionConfigEntryImpl<E, T extends Collection<E>> exten
|
||||
visitor.leaveCollectionEntry(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (visitor.enterCollectionEntry(this, value)) {
|
||||
if (value != null) {
|
||||
for (E element : value) {
|
||||
visitor.visitEntry(elementEntry, element);
|
||||
}
|
||||
}
|
||||
visitor.leaveCollectionEntry(this, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
import de.siphalor.tweed5.core.api.validation.ConfigEntryValueValidationException;
|
||||
import lombok.Getter;
|
||||
import lombok.Value;
|
||||
|
||||
@@ -52,11 +52,8 @@ public class ReflectiveCompoundConfigEntryImpl<T> extends BaseConfigEntryImpl<T>
|
||||
}
|
||||
|
||||
try {
|
||||
compoundEntry.configEntry().validate(value);
|
||||
compoundEntry.field().set(compoundValue, value);
|
||||
|
||||
} catch (ConfigEntryValueValidationException e) {
|
||||
throw new IllegalArgumentException("Invalid value for config entry: " + key, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
@@ -99,6 +96,23 @@ public class ReflectiveCompoundConfigEntryImpl<T> extends BaseConfigEntryImpl<T>
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (visitor.enterCompoundEntry(this, value)) {
|
||||
compoundEntries.forEach((key, entry) -> {
|
||||
if (visitor.enterCompoundSubEntry(key)) {
|
||||
try {
|
||||
visitor.visitEntry(entry.configEntry(), entry.field().get(value));
|
||||
} catch (IllegalAccessException ignored) {
|
||||
// ignored
|
||||
}
|
||||
visitor.leaveCompoundSubEntry(key);
|
||||
}
|
||||
});
|
||||
visitor.leaveCompoundEntry(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
public static class CompoundEntry {
|
||||
String name;
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
||||
|
||||
public class SimpleConfigEntryImpl<T> extends BaseConfigEntryImpl<T> implements SimpleConfigEntry<T> {
|
||||
public SimpleConfigEntryImpl(Class<T> valueClass) {
|
||||
super(valueClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryVisitor visitor) {
|
||||
visitor.visitEntry(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
visitor.visitEntry(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -64,4 +65,20 @@ public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> ext
|
||||
visitor.leaveCompoundEntry(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
|
||||
if (visitor.enterCompoundEntry(this, value)) {
|
||||
if (value != null) {
|
||||
compoundEntries.forEach((key, entry) -> {
|
||||
if (visitor.enterCompoundSubEntry(key)) {
|
||||
//noinspection unchecked
|
||||
((ConfigEntry<Object>) entry).visitInOrder(visitor, value.get(key));
|
||||
visitor.leaveCompoundSubEntry(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
visitor.leaveCompoundEntry(this, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user