[patchwork, core, extensions] Hugely simplify Patchworks
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
package de.siphalor.tweed5.core.api.container;
|
||||
|
||||
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -16,7 +15,7 @@ import java.util.Optional;
|
||||
* @param <T> The class that the config tree represents
|
||||
* @see ConfigContainerSetupPhase
|
||||
*/
|
||||
public interface ConfigContainer<T> {
|
||||
public interface ConfigContainer<T extends @Nullable Object> {
|
||||
@SuppressWarnings("rawtypes")
|
||||
TweedConstructFactory<ConfigContainer> FACTORY = TweedConstructFactory.builder(ConfigContainer.class).build();
|
||||
|
||||
@@ -36,10 +35,9 @@ public interface ConfigContainer<T> {
|
||||
|
||||
void attachTree(ConfigEntry<T> rootEntry);
|
||||
|
||||
EntryExtensionsData createExtensionsData();
|
||||
Patchwork createExtensionsData();
|
||||
|
||||
void initialize();
|
||||
|
||||
ConfigEntry<T> rootEntry();
|
||||
Map<Class<?>, ? extends RegisteredExtensionData<EntryExtensionsData, ?>> entryDataExtensions();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.core.api.container;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -1,14 +1,14 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public abstract class BaseConfigEntry<T> implements ConfigEntry<T> {
|
||||
private final ConfigContainer<?> container;
|
||||
private final Class<T> valueClass;
|
||||
private final EntryExtensionsData extensionsData;
|
||||
private final Patchwork extensionsData;
|
||||
|
||||
public BaseConfigEntry(ConfigContainer<?> container, Class<T> valueClass) {
|
||||
this.container = container;
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface CollectionConfigEntry<E, T extends Collection<E>> extends ConfigEntry<T> {
|
||||
@Override
|
||||
default CollectionConfigEntry<E, T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
ConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
ConfigEntry<E> elementEntry();
|
||||
|
||||
T instantiateCollection(int size);
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface CompoundConfigEntry<T> extends ConfigEntry<T> {
|
||||
@Override
|
||||
default CompoundConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
ConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
|
||||
Map<String, ConfigEntry<?>> subEntries();
|
||||
|
||||
<V> void set(T compoundValue, String key, V value);
|
||||
|
||||
@@ -1,18 +1,32 @@
|
||||
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.patchwork.api.Patchwork;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public interface ConfigEntry<T> {
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface ConfigEntry<T extends @Nullable Object> {
|
||||
|
||||
ConfigContainer<?> container();
|
||||
|
||||
Class<T> valueClass();
|
||||
|
||||
EntryExtensionsData extensionsData();
|
||||
Patchwork extensionsData();
|
||||
|
||||
default ConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
function.accept(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
default <R> R call(Function<ConfigEntry<T>, R> function) {
|
||||
return function.apply(this);
|
||||
}
|
||||
|
||||
void visitInOrder(ConfigEntryVisitor visitor);
|
||||
void visitInOrder(ConfigEntryValueVisitor visitor, T value);
|
||||
void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value);
|
||||
|
||||
T deepCopy(T value);
|
||||
T deepCopy(@NonNull T value);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
package de.siphalor.tweed5.core.api.entry;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface SimpleConfigEntry<T> extends ConfigEntry<T> {
|
||||
@Override
|
||||
default SimpleConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
|
||||
ConfigEntry.super.apply(function);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package de.siphalor.tweed5.core.api.extension;
|
||||
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
|
||||
public interface EntryExtensionsData extends Patchwork<EntryExtensionsData> {
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package de.siphalor.tweed5.core.api.extension;
|
||||
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
|
||||
public interface RegisteredExtensionData<U extends Patchwork<U>, E> {
|
||||
void set(U patchwork, E extension);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package de.siphalor.tweed5.core.api.extension;
|
||||
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
|
||||
public interface TweedExtensionSetupContext {
|
||||
<E> RegisteredExtensionData<EntryExtensionsData, E> registerEntryExtensionData(Class<E> dataClass);
|
||||
<E> PatchworkPartAccess<E> registerEntryExtensionData(Class<E> dataClass);
|
||||
void registerExtension(Class<? extends TweedExtension> extensionClass);
|
||||
}
|
||||
|
||||
@@ -3,21 +3,15 @@ package de.siphalor.tweed5.core.impl;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkClassCreator;
|
||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClass;
|
||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClassGenerator;
|
||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClassPart;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import de.siphalor.tweed5.utils.api.collection.InheritanceMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
@@ -28,9 +22,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
private final Set<Class<? extends TweedExtension>> requestedExtensions = new HashSet<>();
|
||||
private final InheritanceMap<TweedExtension> extensions = new InheritanceMap<>(TweedExtension.class);
|
||||
private @Nullable ConfigEntry<T> rootEntry;
|
||||
private @Nullable PatchworkClass<EntryExtensionsData> entryExtensionsDataPatchworkClass;
|
||||
private final Map<Class<?>, RegisteredExtensionDataImpl<EntryExtensionsData, ?>> registeredEntryDataExtensions
|
||||
= new HashMap<>();
|
||||
private @Nullable PatchworkFactory entryExtensionsDataPatchworkFactory;
|
||||
|
||||
@Override
|
||||
public void registerExtension(Class<? extends TweedExtension> extensionClass) {
|
||||
@@ -42,15 +34,12 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
public void finishExtensionSetup() {
|
||||
requireSetupPhase(ConfigContainerSetupPhase.EXTENSIONS_SETUP);
|
||||
|
||||
PatchworkFactory.Builder entryExtensionDataPatchworkFactoryBuilder = PatchworkFactory.builder();
|
||||
|
||||
TweedExtensionSetupContext extensionSetupContext = new TweedExtensionSetupContext() {
|
||||
@Override
|
||||
public <E> RegisteredExtensionData<EntryExtensionsData, E> registerEntryExtensionData(Class<E> dataClass) {
|
||||
if (registeredEntryDataExtensions.containsKey(dataClass)) {
|
||||
throw new IllegalArgumentException("Extension " + dataClass.getName() + " is already registered");
|
||||
}
|
||||
RegisteredExtensionDataImpl<EntryExtensionsData, E> registered = new RegisteredExtensionDataImpl<>();
|
||||
registeredEntryDataExtensions.put(dataClass, registered);
|
||||
return registered;
|
||||
public <E> PatchworkPartAccess<E> registerEntryExtensionData(Class<E> dataClass) {
|
||||
return entryExtensionDataPatchworkFactoryBuilder.registerPart(dataClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,20 +70,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
}
|
||||
}
|
||||
|
||||
PatchworkClassCreator<EntryExtensionsData> entryExtensionsDataGenerator = PatchworkClassCreator.<EntryExtensionsData>builder()
|
||||
.patchworkInterface(EntryExtensionsData.class)
|
||||
.classPackage("de.siphalor.tweed5.core.generated.entryextensiondata")
|
||||
.classPrefix("EntryExtensionsData$")
|
||||
.build();
|
||||
try {
|
||||
entryExtensionsDataPatchworkClass = entryExtensionsDataGenerator.createClass(registeredEntryDataExtensions.keySet());
|
||||
for (PatchworkClassPart part : entryExtensionsDataPatchworkClass.parts()) {
|
||||
RegisteredExtensionDataImpl<EntryExtensionsData, ?> registeredExtension = registeredEntryDataExtensions.get(part.partInterface());
|
||||
registeredExtension.setter(part.fieldSetter());
|
||||
}
|
||||
} catch (PatchworkClassGenerator.GenerationException e) {
|
||||
throw new IllegalStateException("Failed to create patchwork class for entry extensions' data", e);
|
||||
}
|
||||
entryExtensionsDataPatchworkFactory = entryExtensionDataPatchworkFactoryBuilder.build();
|
||||
|
||||
setupPhase = ConfigContainerSetupPhase.TREE_SETUP;
|
||||
|
||||
@@ -230,25 +206,11 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntryExtensionsData createExtensionsData() {
|
||||
public Patchwork createExtensionsData() {
|
||||
requireSetupPhase(ConfigContainerSetupPhase.TREE_SETUP);
|
||||
|
||||
try {
|
||||
assert entryExtensionsDataPatchworkClass != null;
|
||||
return (EntryExtensionsData) entryExtensionsDataPatchworkClass.constructor().invoke();
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException("Failed to construct patchwork class for entry extensions' data", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Class<?>, ? extends RegisteredExtensionData<EntryExtensionsData, ?>> entryDataExtensions() {
|
||||
requireSetupPhase(
|
||||
ConfigContainerSetupPhase.TREE_SETUP,
|
||||
ConfigContainerSetupPhase.TREE_ATTACHED,
|
||||
ConfigContainerSetupPhase.INITIALIZED
|
||||
);
|
||||
return registeredEntryDataExtensions;
|
||||
assert entryExtensionsDataPatchworkFactory != null;
|
||||
return entryExtensionsDataPatchworkFactory.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,18 +257,4 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Setter
|
||||
private static class RegisteredExtensionDataImpl<U extends Patchwork<U>, E> implements RegisteredExtensionData<U, E> {
|
||||
private MethodHandle setter;
|
||||
|
||||
@Override
|
||||
public void set(U patchwork, E extension) {
|
||||
try {
|
||||
setter.invokeWithArguments(patchwork, extension);
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor;
|
||||
import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public class SimpleConfigEntryImpl<T> extends BaseConfigEntry<T> implements SimpleConfigEntry<T> {
|
||||
public SimpleConfigEntryImpl(ConfigContainer<?> container, Class<T> valueClass) {
|
||||
@@ -22,7 +23,7 @@ public class SimpleConfigEntryImpl<T> extends BaseConfigEntry<T> implements Simp
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deepCopy(T value) {
|
||||
public T deepCopy(@NonNull T value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package de.siphalor.tweed5.core.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
@@ -131,28 +129,7 @@ class DefaultConfigContainerTest {
|
||||
configContainer.registerExtension(ExtensionB.class);
|
||||
configContainer.finishExtensionSetup();
|
||||
var extensionData = configContainer.createExtensionsData();
|
||||
assertThat(extensionData).isNotNull()
|
||||
.satisfies(
|
||||
data -> assertThat(data.isPatchworkPartDefined(ExtensionBData.class)).isTrue(),
|
||||
data -> assertThat(data.isPatchworkPartDefined(String.class)).isFalse()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void entryDataExtensions() {
|
||||
var configContainer = new DefaultConfigContainer<>();
|
||||
configContainer.registerExtension(ExtensionB.class);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
assertThat(configContainer.entryDataExtensions()).containsOnlyKeys(ExtensionBData.class);
|
||||
//noinspection unchecked
|
||||
var registeredExtension = (RegisteredExtensionData<EntryExtensionsData, ExtensionBData>)
|
||||
configContainer.entryDataExtensions().get(ExtensionBData.class);
|
||||
|
||||
var extensionsData = configContainer.createExtensionsData();
|
||||
registeredExtension.set(extensionsData, new ExtensionBDataImpl("blub"));
|
||||
|
||||
assertThat(((ExtensionBData) extensionsData).test()).isEqualTo("blub");
|
||||
assertThat(extensionData).isNotNull();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
Reference in New Issue
Block a user