[*] Remove config entry sealing
This commit is contained in:
@@ -50,6 +50,7 @@ dependencies {
|
||||
testImplementation(libs.mockito)
|
||||
testAgent(libs.mockito)
|
||||
testImplementation(libs.assertj)
|
||||
testImplementation(project(":test-utils"))
|
||||
}
|
||||
|
||||
tasks.compileTestJava {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
rootProject.name = "tweed5"
|
||||
|
||||
include("test-utils")
|
||||
include("tweed5-construct")
|
||||
include("tweed5-core")
|
||||
include("tweed5-default-extensions")
|
||||
|
||||
3
test-utils/build.gradle.kts
Normal file
3
test-utils/build.gradle.kts
Normal file
@@ -0,0 +1,3 @@
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.siphalor.tweed5.testutils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MapTestUtils {
|
||||
public static <K, V> SequencedMap<K, V> sequencedMap(Collection<Map.Entry<K, V>> entries) {
|
||||
var map = LinkedHashMap.<K, V>newLinkedHashMap(entries.size());
|
||||
entries.forEach(entry -> map.put(entry.getKey(), entry.getValue()));
|
||||
return Collections.unmodifiableSequencedMap(map);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ 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 org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
@@ -33,7 +32,7 @@ public interface ConfigContainer<T> {
|
||||
|
||||
void finishExtensionSetup();
|
||||
|
||||
void attachAndSealTree(ConfigEntry<T> rootEntry);
|
||||
void attachTree(ConfigEntry<T> rootEntry);
|
||||
|
||||
EntryExtensionsData createExtensionsData();
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@ package de.siphalor.tweed5.core.api.container;
|
||||
public enum ConfigContainerSetupPhase {
|
||||
EXTENSIONS_SETUP,
|
||||
TREE_SETUP,
|
||||
TREE_SEALED,
|
||||
TREE_ATTACHED,
|
||||
READY,
|
||||
}
|
||||
|
||||
@@ -3,29 +3,16 @@ package de.siphalor.tweed5.core.api.entry;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public abstract class BaseConfigEntry<T> implements ConfigEntry<T> {
|
||||
|
||||
private final ConfigContainer<?> container;
|
||||
private final Class<T> valueClass;
|
||||
private ConfigContainer<?> container;
|
||||
private EntryExtensionsData extensionsData;
|
||||
private boolean sealed;
|
||||
|
||||
@Override
|
||||
public void seal(ConfigContainer<?> container) {
|
||||
requireUnsealed();
|
||||
private final EntryExtensionsData extensionsData;
|
||||
|
||||
public BaseConfigEntry(ConfigContainer<?> container, Class<T> valueClass) {
|
||||
this.container = container;
|
||||
this.valueClass = valueClass;
|
||||
this.extensionsData = container.createExtensionsData();
|
||||
sealed = true;
|
||||
}
|
||||
|
||||
protected void requireUnsealed() {
|
||||
if (sealed) {
|
||||
throw new IllegalStateException("Config entry is already sealed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
|
||||
public interface ConfigEntry<T> {
|
||||
Class<T> valueClass();
|
||||
|
||||
void seal(ConfigContainer<?> container);
|
||||
boolean sealed();
|
||||
ConfigContainer<?> container();
|
||||
|
||||
Class<T> valueClass();
|
||||
|
||||
EntryExtensionsData extensionsData();
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
public <E extends TweedExtension> Optional<E> extension(Class<E> extensionClass) {
|
||||
requireSetupPhase(
|
||||
ConfigContainerSetupPhase.TREE_SETUP,
|
||||
ConfigContainerSetupPhase.TREE_SEALED,
|
||||
ConfigContainerSetupPhase.TREE_ATTACHED,
|
||||
ConfigContainerSetupPhase.READY
|
||||
);
|
||||
try {
|
||||
@@ -214,25 +214,19 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
public Collection<TweedExtension> extensions() {
|
||||
requireSetupPhase(
|
||||
ConfigContainerSetupPhase.TREE_SETUP,
|
||||
ConfigContainerSetupPhase.TREE_SEALED,
|
||||
ConfigContainerSetupPhase.TREE_ATTACHED,
|
||||
ConfigContainerSetupPhase.READY
|
||||
);
|
||||
return Collections.unmodifiableCollection(extensions.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachAndSealTree(ConfigEntry<T> rootEntry) {
|
||||
public void attachTree(ConfigEntry<T> rootEntry) {
|
||||
requireSetupPhase(ConfigContainerSetupPhase.TREE_SETUP);
|
||||
|
||||
this.rootEntry = rootEntry;
|
||||
|
||||
rootEntry.visitInOrder(entry -> {
|
||||
if (!entry.sealed()) {
|
||||
entry.seal(DefaultConfigContainer.this);
|
||||
}
|
||||
});
|
||||
|
||||
setupPhase = ConfigContainerSetupPhase.TREE_SEALED;
|
||||
setupPhase = ConfigContainerSetupPhase.TREE_ATTACHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,7 +245,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
public Map<Class<?>, ? extends RegisteredExtensionData<EntryExtensionsData, ?>> entryDataExtensions() {
|
||||
requireSetupPhase(
|
||||
ConfigContainerSetupPhase.TREE_SETUP,
|
||||
ConfigContainerSetupPhase.TREE_SEALED,
|
||||
ConfigContainerSetupPhase.TREE_ATTACHED,
|
||||
ConfigContainerSetupPhase.READY
|
||||
);
|
||||
return registeredEntryDataExtensions;
|
||||
@@ -259,7 +253,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
requireSetupPhase(ConfigContainerSetupPhase.TREE_SEALED);
|
||||
requireSetupPhase(ConfigContainerSetupPhase.TREE_ATTACHED);
|
||||
|
||||
assert rootEntry != null;
|
||||
rootEntry.visitInOrder(entry -> {
|
||||
@@ -273,7 +267,7 @@ public class DefaultConfigContainer<T> implements ConfigContainer<T> {
|
||||
|
||||
@Override
|
||||
public ConfigEntry<T> rootEntry() {
|
||||
requireSetupPhase(ConfigContainerSetupPhase.TREE_SEALED, ConfigContainerSetupPhase.READY);
|
||||
requireSetupPhase(ConfigContainerSetupPhase.TREE_ATTACHED, ConfigContainerSetupPhase.READY);
|
||||
|
||||
assert rootEntry != null;
|
||||
return rootEntry;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.*;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -7,16 +8,16 @@ import java.util.function.IntFunction;
|
||||
|
||||
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements CollectionConfigEntry<E, T> {
|
||||
private final IntFunction<T> collectionConstructor;
|
||||
private ConfigEntry<E> elementEntry;
|
||||
private final ConfigEntry<E> elementEntry;
|
||||
|
||||
public CollectionConfigEntryImpl(Class<T> valueClass, IntFunction<T> collectionConstructor) {
|
||||
super(valueClass);
|
||||
public CollectionConfigEntryImpl(
|
||||
ConfigContainer<?> container,
|
||||
Class<T> valueClass,
|
||||
IntFunction<T> collectionConstructor,
|
||||
ConfigEntry<E> elementEntry
|
||||
) {
|
||||
super(container, valueClass);
|
||||
this.collectionConstructor = collectionConstructor;
|
||||
}
|
||||
|
||||
public void elementEntry(ConfigEntry<E> elementEntry) {
|
||||
requireUnsealed();
|
||||
|
||||
this.elementEntry = elementEntry;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
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;
|
||||
|
||||
public class SimpleConfigEntryImpl<T> extends BaseConfigEntry<T> implements SimpleConfigEntry<T> {
|
||||
public SimpleConfigEntryImpl(Class<T> valueClass) {
|
||||
super(valueClass);
|
||||
public SimpleConfigEntryImpl(ConfigContainer<?> container, Class<T> valueClass) {
|
||||
super(container, valueClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.siphalor.tweed5.core.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.*;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -8,16 +9,17 @@ import java.util.function.IntFunction;
|
||||
|
||||
public class StaticMapCompoundConfigEntryImpl<T extends Map<String, Object>> extends BaseConfigEntry<T> implements CompoundConfigEntry<T> {
|
||||
private final IntFunction<T> mapConstructor;
|
||||
private final Map<String, ConfigEntry<?>> compoundEntries = new LinkedHashMap<>();
|
||||
private final Map<String, ConfigEntry<?>> compoundEntries;
|
||||
|
||||
public StaticMapCompoundConfigEntryImpl(Class<T> valueClass, IntFunction<T> mapConstructor) {
|
||||
super(valueClass);
|
||||
public StaticMapCompoundConfigEntryImpl(
|
||||
ConfigContainer<?> container,
|
||||
Class<T> valueClass,
|
||||
IntFunction<T> mapConstructor,
|
||||
Map<String, ConfigEntry<?>> compoundEntries
|
||||
) {
|
||||
super(container, valueClass);
|
||||
this.mapConstructor = mapConstructor;
|
||||
}
|
||||
|
||||
public void addSubEntry(String key, ConfigEntry<?> entry) {
|
||||
requireUnsealed();
|
||||
compoundEntries.put(key, entry);
|
||||
this.compoundEntries = new LinkedHashMap<>(compoundEntries);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -105,23 +105,23 @@ class DefaultConfigContainerTest {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void attachAndSealTree() {
|
||||
void attachTree() {
|
||||
var configContainer = new DefaultConfigContainer<Map<String, Object>>();
|
||||
var compoundEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
(Class<Map<String, Object>>)(Class<?>) Map.class,
|
||||
(capacity) -> new HashMap<>(capacity * 2, 0.5F)
|
||||
);
|
||||
var subEntry = new SimpleConfigEntryImpl<>(String.class);
|
||||
compoundEntry.addSubEntry("test", subEntry);
|
||||
|
||||
configContainer.registerExtension(ExtensionInitTracker.class);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
assertThat(configContainer.setupPhase()).isEqualTo(ConfigContainerSetupPhase.TREE_SETUP);
|
||||
configContainer.attachAndSealTree(compoundEntry);
|
||||
var subEntry = new SimpleConfigEntryImpl<>(configContainer, String.class);
|
||||
var compoundEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
configContainer,
|
||||
(Class<Map<String, Object>>)(Class<?>) Map.class,
|
||||
(capacity) -> new HashMap<>(capacity * 2, 0.5F),
|
||||
Map.of("test", subEntry)
|
||||
);
|
||||
|
||||
assertThat(configContainer.setupPhase()).isEqualTo(ConfigContainerSetupPhase.TREE_SEALED);
|
||||
assertThat(compoundEntry.sealed()).isTrue();
|
||||
assertThat(subEntry.sealed()).isTrue();
|
||||
assertThat(configContainer.setupPhase()).isEqualTo(ConfigContainerSetupPhase.TREE_SETUP);
|
||||
configContainer.attachTree(compoundEntry);
|
||||
|
||||
assertThat(configContainer.setupPhase()).isEqualTo(ConfigContainerSetupPhase.TREE_ATTACHED);
|
||||
assertThat(configContainer.rootEntry()).isSameAs(compoundEntry);
|
||||
}
|
||||
|
||||
@@ -155,21 +155,23 @@ class DefaultConfigContainerTest {
|
||||
assertThat(((ExtensionBData) extensionsData).test()).isEqualTo("blub");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
void initialize() {
|
||||
var configContainer = new DefaultConfigContainer<Map<String, Object>>();
|
||||
var compoundEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
(Class<Map<String, Object>>)(Class<?>) Map.class,
|
||||
(capacity) -> new HashMap<>(capacity * 2, 0.5F)
|
||||
);
|
||||
var subEntry = new SimpleConfigEntryImpl<>(String.class);
|
||||
compoundEntry.addSubEntry("test", subEntry);
|
||||
|
||||
configContainer.registerExtension(ExtensionInitTracker.class);
|
||||
configContainer.finishExtensionSetup();
|
||||
configContainer.attachAndSealTree(compoundEntry);
|
||||
|
||||
assertThat(configContainer.setupPhase()).isEqualTo(ConfigContainerSetupPhase.TREE_SEALED);
|
||||
var subEntry = new SimpleConfigEntryImpl<>(configContainer, String.class);
|
||||
var compoundEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
configContainer,
|
||||
(Class<Map<String, Object>>)(Class<?>) Map.class,
|
||||
(capacity) -> new HashMap<>(capacity * 2, 0.5F),
|
||||
Map.of("test", subEntry)
|
||||
);
|
||||
configContainer.attachTree(compoundEntry);
|
||||
|
||||
assertThat(configContainer.setupPhase()).isEqualTo(ConfigContainerSetupPhase.TREE_ATTACHED);
|
||||
configContainer.initialize();
|
||||
assertThat(configContainer.setupPhase()).isEqualTo(ConfigContainerSetupPhase.READY);
|
||||
|
||||
|
||||
@@ -28,8 +28,11 @@ import org.junit.jupiter.api.Test;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static de.siphalor.tweed5.testutils.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@NullUnmarked
|
||||
@@ -49,17 +52,23 @@ class CommentExtensionImplTest {
|
||||
configContainer.registerExtensions(extraExtensions);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class);
|
||||
stringEntry = new SimpleConfigEntryImpl<>(configContainer, String.class);
|
||||
noCommentEntry = new SimpleConfigEntryImpl<>(configContainer, Long.class);
|
||||
|
||||
//noinspection unchecked
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(((Class<Map<String, Object>>)(Class<?>) Map.class), LinkedHashMap::new);
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
configContainer,
|
||||
((Class<Map<String, Object>>)(Class<?>) Map.class),
|
||||
LinkedHashMap::new,
|
||||
sequencedMap(List.of(
|
||||
entry("int", intEntry),
|
||||
entry("string", stringEntry),
|
||||
entry("noComment", noCommentEntry)
|
||||
))
|
||||
);
|
||||
|
||||
intEntry = new SimpleConfigEntryImpl<>(Integer.class);
|
||||
rootEntry.addSubEntry("int", intEntry);
|
||||
stringEntry = new SimpleConfigEntryImpl<>(String.class);
|
||||
rootEntry.addSubEntry("string", stringEntry);
|
||||
noCommentEntry = new SimpleConfigEntryImpl<>(Long.class);
|
||||
rootEntry.addSubEntry("noComment", noCommentEntry);
|
||||
|
||||
configContainer.attachAndSealTree(rootEntry);
|
||||
configContainer.attachTree(rootEntry);
|
||||
|
||||
//noinspection unchecked
|
||||
RegisteredExtensionData<EntryExtensionsData, EntryComment> commentData = (RegisteredExtensionData<EntryExtensionsData, EntryComment>) configContainer.entryDataExtensions().get(EntryComment.class);
|
||||
|
||||
@@ -20,11 +20,10 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static de.siphalor.tweed5.testutils.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ValidationExtensionImplTest {
|
||||
@@ -42,17 +41,24 @@ class ValidationExtensionImplTest {
|
||||
configContainer.registerExtension(ValidationExtension.DEFAULT);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
byteEntry = new SimpleConfigEntryImpl<>(configContainer, Byte.class);
|
||||
intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class);
|
||||
doubleEntry = new SimpleConfigEntryImpl<>(configContainer, Double.class);
|
||||
|
||||
//noinspection unchecked
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(((Class<Map<String, Object>>) (Class<?>) Map.class), LinkedHashMap::new);
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
configContainer,
|
||||
((Class<Map<String, Object>>) (Class<?>) Map.class),
|
||||
LinkedHashMap::new,
|
||||
sequencedMap(List.of(
|
||||
entry("byte", byteEntry),
|
||||
entry("int", intEntry),
|
||||
entry("double", doubleEntry)
|
||||
))
|
||||
);
|
||||
|
||||
byteEntry = new SimpleConfigEntryImpl<>(Byte.class);
|
||||
rootEntry.addSubEntry("byte", byteEntry);
|
||||
intEntry = new SimpleConfigEntryImpl<>(Integer.class);
|
||||
rootEntry.addSubEntry("int", intEntry);
|
||||
doubleEntry = new SimpleConfigEntryImpl<>(Double.class);
|
||||
rootEntry.addSubEntry("double", doubleEntry);
|
||||
|
||||
configContainer.attachAndSealTree(rootEntry);
|
||||
configContainer.attachTree(rootEntry);
|
||||
|
||||
//noinspection unchecked
|
||||
RegisteredExtensionData<EntryExtensionsData, EntryComment> commentData = (RegisteredExtensionData<EntryExtensionsData, EntryComment>) configContainer.entryDataExtensions().get(EntryComment.class);
|
||||
|
||||
@@ -55,9 +55,8 @@ class ValidationFallbackExtensionImplTest {
|
||||
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
intEntry = new SimpleConfigEntryImpl<>(Integer.class);
|
||||
|
||||
configContainer.attachAndSealTree(intEntry);
|
||||
intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class);
|
||||
configContainer.attachTree(intEntry);
|
||||
|
||||
RegisteredExtensionData<EntryExtensionsData, EntrySpecificValidation> entrySpecificValidation = (RegisteredExtensionData<EntryExtensionsData, EntrySpecificValidation>) configContainer.entryDataExtensions().get(EntrySpecificValidation.class);
|
||||
entrySpecificValidation.set(intEntry.extensionsData(), () -> Arrays.asList(
|
||||
|
||||
@@ -8,9 +8,9 @@ import de.siphalor.tweed5.core.impl.entry.CollectionConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.EntryReaderWriterDefinition;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
@@ -27,6 +27,8 @@ import java.io.Writer;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static de.siphalor.tweed5.testutils.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -42,18 +44,26 @@ class ReadWriteExtensionImplTest {
|
||||
configContainer.registerExtension(ReadWriteExtension.DEFAULT);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(((Class<Map<String, Object>>) (Class<?>) Map.class), LinkedHashMap::new);
|
||||
SimpleConfigEntryImpl<Integer> intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class);
|
||||
SimpleConfigEntryImpl<Boolean> booleanEntry = new SimpleConfigEntryImpl<>(configContainer, Boolean.class);
|
||||
CollectionConfigEntryImpl<Boolean, List<Boolean>> listEntry = new CollectionConfigEntryImpl<>(
|
||||
configContainer,
|
||||
(Class<List<Boolean>>) (Class<?>) List.class,
|
||||
ArrayList::new,
|
||||
booleanEntry
|
||||
);
|
||||
|
||||
SimpleConfigEntryImpl<Integer> intEntry = new SimpleConfigEntryImpl<>(Integer.class);
|
||||
rootEntry.addSubEntry("int", intEntry);
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
configContainer,
|
||||
((Class<Map<String, Object>>) (Class<?>) Map.class),
|
||||
LinkedHashMap::new,
|
||||
sequencedMap(List.of(
|
||||
entry("int", intEntry),
|
||||
entry("list", listEntry)
|
||||
))
|
||||
);
|
||||
|
||||
CollectionConfigEntryImpl<Boolean, List<Boolean>> listEntry = new CollectionConfigEntryImpl<>((Class<List<Boolean>>) (Class<?>) List.class, ArrayList::new);
|
||||
rootEntry.addSubEntry("list", listEntry);
|
||||
|
||||
SimpleConfigEntryImpl<Boolean> booleanEntry = new SimpleConfigEntryImpl<>(Boolean.class);
|
||||
listEntry.elementEntry(booleanEntry);
|
||||
|
||||
configContainer.attachAndSealTree(rootEntry);
|
||||
configContainer.attachTree(rootEntry);
|
||||
|
||||
RegisteredExtensionData<EntryExtensionsData, EntryReaderWriterDefinition> readerWriterData = (RegisteredExtensionData<EntryExtensionsData, EntryReaderWriterDefinition>) configContainer.entryDataExtensions().get(EntryReaderWriterDefinition.class);
|
||||
readerWriterData.set(rootEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.compoundReaderWriter()));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.entry;
|
||||
|
||||
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.CollectionConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
|
||||
@@ -17,9 +18,9 @@ public interface WeavableCollectionConfigEntry<E, T extends Collection<E>> exten
|
||||
@SuppressWarnings("rawtypes")
|
||||
TweedConstructFactory<WeavableCollectionConfigEntry> FACTORY =
|
||||
TweedConstructFactory.builder(WeavableCollectionConfigEntry.class)
|
||||
.typedArg(ConfigContainer.class)
|
||||
.typedArg(Class.class) // value class
|
||||
.typedArg(IntFunction.class) // value class constructor with capacity
|
||||
.namedArg("elementEntry", ConfigEntry.class)
|
||||
.build();
|
||||
|
||||
void elementEntry(ConfigEntry<E> elementEntry);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.entry;
|
||||
|
||||
import de.siphalor.tweed5.construct.api.TweedConstructFactory;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -8,6 +9,8 @@ import lombok.Value;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -19,12 +22,12 @@ public interface WeavableCompoundConfigEntry<T> extends CompoundConfigEntry<T> {
|
||||
@SuppressWarnings("rawtypes")
|
||||
TweedConstructFactory<WeavableCompoundConfigEntry> FACTORY =
|
||||
TweedConstructFactory.builder(WeavableCompoundConfigEntry.class)
|
||||
.typedArg(ConfigContainer.class)
|
||||
.typedArg(Class.class) // the value class
|
||||
.typedArg(Supplier.class) // constructor for the value class
|
||||
.namedArg("subEntries", List.class) // List of SubEntry's
|
||||
.build();
|
||||
|
||||
void registerSubEntry(SubEntry subEntry);
|
||||
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
class SubEntry {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.typeutils.api.type.ActualType;
|
||||
@@ -44,22 +45,21 @@ public class CollectionPojoWeaver implements TweedPojoWeaver {
|
||||
|
||||
IntFunction<Collection<Object>> constructor = getCollectionConstructor(valueType);
|
||||
|
||||
WeavableCollectionConfigEntry configEntry = WeavableCollectionConfigEntry.FACTORY
|
||||
.construct(Objects.requireNonNull(weavingConfig.collectionEntryClass()))
|
||||
.typedArg(valueType.declaredType())
|
||||
.typedArg(IntFunction.class, constructor)
|
||||
.finish();
|
||||
|
||||
configEntry.elementEntry(context.weaveEntry(
|
||||
ConfigEntry<?> elementEntry = context.weaveEntry(
|
||||
collectionTypeParams.get(0),
|
||||
context.subContextBuilder("element")
|
||||
.annotations(collectionTypeParams.get(0))
|
||||
.extensionsData(newExtensionsData)
|
||||
.build()
|
||||
));
|
||||
configEntry.seal(context.configContainer());
|
||||
);
|
||||
|
||||
return configEntry;
|
||||
return WeavableCollectionConfigEntry.FACTORY
|
||||
.construct(Objects.requireNonNull(weavingConfig.collectionEntryClass()))
|
||||
.typedArg(ConfigContainer.class, context.configContainer())
|
||||
.typedArg(valueType.declaredType())
|
||||
.typedArg(IntFunction.class, constructor)
|
||||
.namedArg("elementEntry", elementEntry)
|
||||
.finish();
|
||||
} catch (Exception e) {
|
||||
throw new PojoWeavingException("Exception occurred trying to weave collection for class " + valueType, e);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.api.weaving;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.namingformat.api.NamingFormat;
|
||||
@@ -20,8 +21,10 @@ import org.jspecify.annotations.Nullable;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A weaver that weaves classes with the {@link CompoundWeaving} annotation as compound entries.
|
||||
@@ -54,18 +57,12 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
||||
|
||||
PojoClassIntrospector introspector = PojoClassIntrospector.forClass(valueType.declaredType());
|
||||
|
||||
WeavableCompoundConfigEntry<T> compoundEntry = instantiateCompoundEntry(introspector, weavingConfig);
|
||||
List<WeavableCompoundConfigEntry.SubEntry> subEntries = introspector.properties().values().stream()
|
||||
.filter(this::shouldIncludeCompoundPropertyInWeaving)
|
||||
.map(property -> weaveCompoundSubEntry(property, newExtensionsData, context))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, PojoClassIntrospector.Property> properties = introspector.properties();
|
||||
properties.forEach((name, property) -> {
|
||||
if (shouldIncludeCompoundPropertyInWeaving(property)) {
|
||||
compoundEntry.registerSubEntry(weaveCompoundSubEntry(property, newExtensionsData, context));
|
||||
}
|
||||
});
|
||||
|
||||
compoundEntry.seal(context.configContainer());
|
||||
|
||||
return compoundEntry;
|
||||
return instantiateCompoundEntry(introspector, weavingConfig, subEntries, context);
|
||||
} catch (Exception e) {
|
||||
throw new PojoWeavingException("Exception occurred trying to weave compound for class " + valueType, e);
|
||||
}
|
||||
@@ -109,7 +106,9 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
||||
@SuppressWarnings("unchecked")
|
||||
private <C> WeavableCompoundConfigEntry<C> instantiateCompoundEntry(
|
||||
PojoClassIntrospector classIntrospector,
|
||||
CompoundWeavingConfig weavingConfig
|
||||
CompoundWeavingConfig weavingConfig,
|
||||
List<WeavableCompoundConfigEntry.SubEntry> subEntries,
|
||||
WeavingContext weavingContext
|
||||
) {
|
||||
MethodHandle valueConstructorHandle = classIntrospector.noArgsConstructor();
|
||||
if (valueConstructorHandle == null) {
|
||||
@@ -131,8 +130,10 @@ public class CompoundPojoWeaver implements TweedPojoWeaver {
|
||||
: StaticPojoCompoundConfigEntry.class
|
||||
);
|
||||
return WeavableCompoundConfigEntry.FACTORY.construct(weavableEntryClass)
|
||||
.typedArg(ConfigContainer.class, weavingContext.configContainer())
|
||||
.typedArg(classIntrospector.type())
|
||||
.typedArg(Supplier.class, valueConstructor)
|
||||
.namedArg("subEntries", subEntries)
|
||||
.finish();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ public class TrivialPojoWeaver implements TweedPojoWeaver {
|
||||
|
||||
@Override
|
||||
public <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, WeavingContext context) {
|
||||
SimpleConfigEntryImpl<T> entry = new SimpleConfigEntryImpl<>(valueType.declaredType());
|
||||
entry.seal(context.configContainer());
|
||||
return entry;
|
||||
return new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ import org.jspecify.annotations.Nullable;
|
||||
public interface TweedPojoWeavingFunction {
|
||||
/**
|
||||
* Weaves a {@link ConfigEntry} for the given value class and context.
|
||||
* The returned config entry must be sealed.
|
||||
* @return The resulting, sealed config entry or {@code null}, if the weaving function is not applicable to the given parameters.
|
||||
* @return The resulting config entry or {@code null}, if the weaving function is not applicable to the given parameters.
|
||||
*/
|
||||
<T> @Nullable ConfigEntry<T> weaveEntry(ActualType<T> valueType, WeavingContext context);
|
||||
|
||||
@@ -19,7 +18,7 @@ public interface TweedPojoWeavingFunction {
|
||||
* {@inheritDoc}
|
||||
* <br />
|
||||
* The function must ensure that the resulting entry is not null, e.g., by trowing a {@link RuntimeException}.
|
||||
* @return The resulting, sealed config entry.
|
||||
* @return The resulting config entry.
|
||||
* @throws RuntimeException when a valid config entry could not be resolved.
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.construct.api.ConstructParameter;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
@@ -19,15 +21,16 @@ import java.util.function.IntFunction;
|
||||
@ToString(callSuper = true)
|
||||
public class CollectionConfigEntryImpl<E, T extends Collection<E>> extends BaseConfigEntry<T> implements WeavableCollectionConfigEntry<E, T> {
|
||||
private final IntFunction<T> constructor;
|
||||
private @Nullable ConfigEntry<E> elementEntry;
|
||||
private final @Nullable ConfigEntry<E> elementEntry;
|
||||
|
||||
public CollectionConfigEntryImpl(@NotNull Class<T> valueClass, IntFunction<T> constructor) {
|
||||
super(valueClass);
|
||||
public CollectionConfigEntryImpl(
|
||||
ConfigContainer<?> configContainer,
|
||||
Class<T> valueClass,
|
||||
IntFunction<T> constructor,
|
||||
@ConstructParameter(name = "elementEntry") ConfigEntry<E> elementEntry
|
||||
) {
|
||||
super(configContainer, valueClass);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void elementEntry(ConfigEntry<E> elementEntry) {
|
||||
this.elementEntry = elementEntry;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package de.siphalor.tweed5.weaver.pojo.impl.entry;
|
||||
|
||||
import de.siphalor.tweed5.construct.api.ConstructParameter;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.BaseConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor;
|
||||
@@ -8,22 +10,32 @@ import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCompoundConfigEntry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> implements WeavableCompoundConfigEntry<T> {
|
||||
private final Supplier<T> noArgsConstructor;
|
||||
private final Map<String, SubEntry> subEntries = new LinkedHashMap<>();
|
||||
private final Map<String, ConfigEntry<?>> subConfigEntries = new LinkedHashMap<>();
|
||||
private final Map<String, SubEntry> subEntries;
|
||||
private final Map<String, ConfigEntry<?>> subConfigEntries;
|
||||
|
||||
public StaticPojoCompoundConfigEntry(Class<T> valueClass, Supplier<T> noArgsConstructor) {
|
||||
super(valueClass);
|
||||
public StaticPojoCompoundConfigEntry(
|
||||
ConfigContainer<?> configContainer,
|
||||
Class<T> valueClass,
|
||||
Supplier<T> noArgsConstructor,
|
||||
@ConstructParameter(name = "subEntries") List<SubEntry> subEntries
|
||||
) {
|
||||
super(configContainer, valueClass);
|
||||
this.noArgsConstructor = noArgsConstructor;
|
||||
this.subEntries = new LinkedHashMap<>(subEntries.size(), 1);
|
||||
this.subConfigEntries = new LinkedHashMap<>(subEntries.size(), 1);
|
||||
for (SubEntry subEntry : subEntries) {
|
||||
this.subEntries.put(subEntry.name(), subEntry);
|
||||
this.subConfigEntries.put(subEntry.name(), subEntry.configEntry());
|
||||
}
|
||||
}
|
||||
|
||||
public void registerSubEntry(SubEntry subEntry) {
|
||||
requireUnsealed();
|
||||
|
||||
subEntries.put(subEntry.name(), subEntry);
|
||||
subConfigEntries.put(subEntry.name(), subEntry.configEntry());
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public class TweedPojoWeaverBootstrapper<T> {
|
||||
WeavingContext weavingContext = createWeavingContext();
|
||||
|
||||
ConfigEntry<T> rootEntry = this.weaveEntry(ActualType.ofClass(pojoClass), weavingContext);
|
||||
configContainer.attachAndSealTree(rootEntry);
|
||||
configContainer.attachTree(rootEntry);
|
||||
|
||||
return configContainer;
|
||||
}
|
||||
@@ -140,9 +140,6 @@ public class TweedPojoWeaverBootstrapper<T> {
|
||||
for (TweedPojoWeaver weaver : weavers) {
|
||||
ConfigEntry<U> configEntry = weaver.weaveEntry(dataClass, context);
|
||||
if (configEntry != null) {
|
||||
if (!configEntry.sealed()) {
|
||||
configEntry.seal(configContainer);
|
||||
}
|
||||
applyPostProcessors(configEntry, context);
|
||||
return configEntry;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ class CompoundPojoWeaverTest {
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
} else {
|
||||
return new SimpleConfigEntryImpl<>(valueType.declaredType());
|
||||
return new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType());
|
||||
}
|
||||
}
|
||||
}, mock(ConfigContainer.class))
|
||||
|
||||
@@ -36,8 +36,6 @@ class TweedPojoWeaverBootstrapperTest {
|
||||
.hasSize(5)
|
||||
));
|
||||
|
||||
configContainer.initialize();
|
||||
|
||||
assertThat(configContainer.extensions())
|
||||
.satisfiesOnlyOnce(extension -> assertThat(extension).isInstanceOf(DummyExtension.class))
|
||||
.hasSize(1);
|
||||
|
||||
Reference in New Issue
Block a user