From 2096ae540cc54ab42d87e08adccf7c7add8ce550 Mon Sep 17 00:00:00 2001 From: Siphalor Date: Tue, 10 Jun 2025 00:46:03 +0200 Subject: [PATCH] [*] Remove config entry sealing --- .../de.siphalor.tweed5.base-module.gradle.kts | 1 + settings.gradle.kts | 1 + test-utils/build.gradle.kts | 3 ++ .../tweed5/testutils/MapTestUtils.java | 11 +++++ .../core/api/container/ConfigContainer.java | 3 +- .../container/ConfigContainerSetupPhase.java | 2 +- .../core/api/entry/BaseConfigEntry.java | 21 ++------- .../tweed5/core/api/entry/ConfigEntry.java | 6 +-- .../core/impl/DefaultConfigContainer.java | 20 +++----- .../impl/entry/CollectionConfigEntryImpl.java | 17 +++---- .../impl/entry/SimpleConfigEntryImpl.java | 5 +- .../StaticMapCompoundConfigEntryImpl.java | 18 ++++---- .../core/impl/DefaultConfigContainerTest.java | 46 ++++++++++--------- .../impl/CommentExtensionImplTest.java | 27 +++++++---- .../impl/ValidationExtensionImplTest.java | 30 +++++++----- .../ValidationFallbackExtensionImplTest.java | 5 +- .../impl/ReadWriteExtensionImplTest.java | 32 ++++++++----- .../entry/WeavableCollectionConfigEntry.java | 5 +- .../entry/WeavableCompoundConfigEntry.java | 7 ++- .../api/weaving/CollectionPojoWeaver.java | 20 ++++---- .../pojo/api/weaving/CompoundPojoWeaver.java | 25 +++++----- .../pojo/api/weaving/TrivialPojoWeaver.java | 4 +- .../api/weaving/TweedPojoWeavingFunction.java | 5 +- .../impl/entry/CollectionConfigEntryImpl.java | 17 ++++--- .../entry/StaticPojoCompoundConfigEntry.java | 24 +++++++--- .../weaving/TweedPojoWeaverBootstrapper.java | 5 +- .../api/weaving/CompoundPojoWeaverTest.java | 2 +- .../TweedPojoWeaverBootstrapperTest.java | 2 - 28 files changed, 201 insertions(+), 163 deletions(-) create mode 100644 test-utils/build.gradle.kts create mode 100644 test-utils/src/main/java/de/siphalor/tweed5/testutils/MapTestUtils.java diff --git a/buildSrc/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts b/buildSrc/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts index 44d40bc..858a855 100644 --- a/buildSrc/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts +++ b/buildSrc/src/main/kotlin/de.siphalor.tweed5.base-module.gradle.kts @@ -50,6 +50,7 @@ dependencies { testImplementation(libs.mockito) testAgent(libs.mockito) testImplementation(libs.assertj) + testImplementation(project(":test-utils")) } tasks.compileTestJava { diff --git a/settings.gradle.kts b/settings.gradle.kts index d3199f0..5b6e522 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,6 @@ rootProject.name = "tweed5" +include("test-utils") include("tweed5-construct") include("tweed5-core") include("tweed5-default-extensions") diff --git a/test-utils/build.gradle.kts b/test-utils/build.gradle.kts new file mode 100644 index 0000000..401a1f9 --- /dev/null +++ b/test-utils/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + java +} diff --git a/test-utils/src/main/java/de/siphalor/tweed5/testutils/MapTestUtils.java b/test-utils/src/main/java/de/siphalor/tweed5/testutils/MapTestUtils.java new file mode 100644 index 0000000..281b317 --- /dev/null +++ b/test-utils/src/main/java/de/siphalor/tweed5/testutils/MapTestUtils.java @@ -0,0 +1,11 @@ +package de.siphalor.tweed5.testutils; + +import java.util.*; + +public class MapTestUtils { + public static SequencedMap sequencedMap(Collection> entries) { + var map = LinkedHashMap.newLinkedHashMap(entries.size()); + entries.forEach(entry -> map.put(entry.getKey(), entry.getValue())); + return Collections.unmodifiableSequencedMap(map); + } +} diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainer.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainer.java index ebca342..8ce372d 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainer.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainer.java @@ -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 { void finishExtensionSetup(); - void attachAndSealTree(ConfigEntry rootEntry); + void attachTree(ConfigEntry rootEntry); EntryExtensionsData createExtensionsData(); diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainerSetupPhase.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainerSetupPhase.java index c9ca07f..01a8991 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainerSetupPhase.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/container/ConfigContainerSetupPhase.java @@ -3,6 +3,6 @@ package de.siphalor.tweed5.core.api.container; public enum ConfigContainerSetupPhase { EXTENSIONS_SETUP, TREE_SETUP, - TREE_SEALED, + TREE_ATTACHED, READY, } diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/BaseConfigEntry.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/BaseConfigEntry.java index 96086c2..acf72d6 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/BaseConfigEntry.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/BaseConfigEntry.java @@ -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 implements ConfigEntry { - + private final ConfigContainer container; private final Class 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 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!"); - } } } diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java index ca05282..4f333dd 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java @@ -4,10 +4,10 @@ import de.siphalor.tweed5.core.api.extension.EntryExtensionsData; import de.siphalor.tweed5.core.api.container.ConfigContainer; public interface ConfigEntry { - Class valueClass(); - void seal(ConfigContainer container); - boolean sealed(); + ConfigContainer container(); + + Class valueClass(); EntryExtensionsData extensionsData(); diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java index 23f493c..f336316 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java @@ -200,7 +200,7 @@ public class DefaultConfigContainer implements ConfigContainer { public Optional extension(Class extensionClass) { requireSetupPhase( ConfigContainerSetupPhase.TREE_SETUP, - ConfigContainerSetupPhase.TREE_SEALED, + ConfigContainerSetupPhase.TREE_ATTACHED, ConfigContainerSetupPhase.READY ); try { @@ -214,25 +214,19 @@ public class DefaultConfigContainer implements ConfigContainer { public Collection extensions() { requireSetupPhase( ConfigContainerSetupPhase.TREE_SETUP, - ConfigContainerSetupPhase.TREE_SEALED, + ConfigContainerSetupPhase.TREE_ATTACHED, ConfigContainerSetupPhase.READY ); return Collections.unmodifiableCollection(extensions.values()); } @Override - public void attachAndSealTree(ConfigEntry rootEntry) { + public void attachTree(ConfigEntry 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 implements ConfigContainer { public Map, ? extends RegisteredExtensionData> entryDataExtensions() { requireSetupPhase( ConfigContainerSetupPhase.TREE_SETUP, - ConfigContainerSetupPhase.TREE_SEALED, + ConfigContainerSetupPhase.TREE_ATTACHED, ConfigContainerSetupPhase.READY ); return registeredEntryDataExtensions; @@ -259,7 +253,7 @@ public class DefaultConfigContainer implements ConfigContainer { @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 implements ConfigContainer { @Override public ConfigEntry rootEntry() { - requireSetupPhase(ConfigContainerSetupPhase.TREE_SEALED, ConfigContainerSetupPhase.READY); + requireSetupPhase(ConfigContainerSetupPhase.TREE_ATTACHED, ConfigContainerSetupPhase.READY); assert rootEntry != null; return rootEntry; diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/CollectionConfigEntryImpl.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/CollectionConfigEntryImpl.java index c52dd07..20c8992 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/CollectionConfigEntryImpl.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/CollectionConfigEntryImpl.java @@ -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> extends BaseConfigEntry implements CollectionConfigEntry { private final IntFunction collectionConstructor; - private ConfigEntry elementEntry; + private final ConfigEntry elementEntry; - public CollectionConfigEntryImpl(Class valueClass, IntFunction collectionConstructor) { - super(valueClass); + public CollectionConfigEntryImpl( + ConfigContainer container, + Class valueClass, + IntFunction collectionConstructor, + ConfigEntry elementEntry + ) { + super(container, valueClass); this.collectionConstructor = collectionConstructor; - } - - public void elementEntry(ConfigEntry elementEntry) { - requireUnsealed(); - this.elementEntry = elementEntry; } diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/SimpleConfigEntryImpl.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/SimpleConfigEntryImpl.java index 62d227a..46c0243 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/SimpleConfigEntryImpl.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/SimpleConfigEntryImpl.java @@ -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 extends BaseConfigEntry implements SimpleConfigEntry { - public SimpleConfigEntryImpl(Class valueClass) { - super(valueClass); + public SimpleConfigEntryImpl(ConfigContainer container, Class valueClass) { + super(container, valueClass); } @Override diff --git a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/StaticMapCompoundConfigEntryImpl.java b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/StaticMapCompoundConfigEntryImpl.java index 2bc6a85..d233fe1 100644 --- a/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/StaticMapCompoundConfigEntryImpl.java +++ b/tweed5-core/src/main/java/de/siphalor/tweed5/core/impl/entry/StaticMapCompoundConfigEntryImpl.java @@ -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> extends BaseConfigEntry implements CompoundConfigEntry { private final IntFunction mapConstructor; - private final Map> compoundEntries = new LinkedHashMap<>(); + private final Map> compoundEntries; - public StaticMapCompoundConfigEntryImpl(Class valueClass, IntFunction mapConstructor) { - super(valueClass); + public StaticMapCompoundConfigEntryImpl( + ConfigContainer container, + Class valueClass, + IntFunction mapConstructor, + Map> 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 diff --git a/tweed5-core/src/test/java/de/siphalor/tweed5/core/impl/DefaultConfigContainerTest.java b/tweed5-core/src/test/java/de/siphalor/tweed5/core/impl/DefaultConfigContainerTest.java index 1a8bb8c..edc5985 100644 --- a/tweed5-core/src/test/java/de/siphalor/tweed5/core/impl/DefaultConfigContainerTest.java +++ b/tweed5-core/src/test/java/de/siphalor/tweed5/core/impl/DefaultConfigContainerTest.java @@ -105,23 +105,23 @@ class DefaultConfigContainerTest { @SuppressWarnings("unchecked") @Test - void attachAndSealTree() { + void attachTree() { var configContainer = new DefaultConfigContainer>(); - var compoundEntry = new StaticMapCompoundConfigEntryImpl<>( - (Class>)(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>)(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>(); - var compoundEntry = new StaticMapCompoundConfigEntryImpl<>( - (Class>)(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>)(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); diff --git a/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java b/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java index 72c6a83..bb9d4d6 100644 --- a/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java +++ b/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java @@ -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>)(Class) Map.class), LinkedHashMap::new); + rootEntry = new StaticMapCompoundConfigEntryImpl<>( + configContainer, + ((Class>)(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 commentData = (RegisteredExtensionData) configContainer.entryDataExtensions().get(EntryComment.class); diff --git a/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImplTest.java b/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImplTest.java index 7f75218..304ae50 100644 --- a/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImplTest.java +++ b/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validation/impl/ValidationExtensionImplTest.java @@ -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>) (Class) Map.class), LinkedHashMap::new); + rootEntry = new StaticMapCompoundConfigEntryImpl<>( + configContainer, + ((Class>) (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 commentData = (RegisteredExtensionData) configContainer.entryDataExtensions().get(EntryComment.class); diff --git a/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validationfallback/impl/ValidationFallbackExtensionImplTest.java b/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validationfallback/impl/ValidationFallbackExtensionImplTest.java index a4778cd..c2e1234 100644 --- a/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validationfallback/impl/ValidationFallbackExtensionImplTest.java +++ b/tweed5-default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/validationfallback/impl/ValidationFallbackExtensionImplTest.java @@ -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 entrySpecificValidation = (RegisteredExtensionData) configContainer.entryDataExtensions().get(EntrySpecificValidation.class); entrySpecificValidation.set(intEntry.extensionsData(), () -> Arrays.asList( diff --git a/tweed5-serde-extension/src/test/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImplTest.java b/tweed5-serde-extension/src/test/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImplTest.java index 31832d9..18b335d 100644 --- a/tweed5-serde-extension/src/test/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImplTest.java +++ b/tweed5-serde-extension/src/test/java/de/siphalor/tweed5/data/extension/impl/ReadWriteExtensionImplTest.java @@ -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>) (Class) Map.class), LinkedHashMap::new); + SimpleConfigEntryImpl intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class); + SimpleConfigEntryImpl booleanEntry = new SimpleConfigEntryImpl<>(configContainer, Boolean.class); + CollectionConfigEntryImpl> listEntry = new CollectionConfigEntryImpl<>( + configContainer, + (Class>) (Class) List.class, + ArrayList::new, + booleanEntry + ); - SimpleConfigEntryImpl intEntry = new SimpleConfigEntryImpl<>(Integer.class); - rootEntry.addSubEntry("int", intEntry); + rootEntry = new StaticMapCompoundConfigEntryImpl<>( + configContainer, + ((Class>) (Class) Map.class), + LinkedHashMap::new, + sequencedMap(List.of( + entry("int", intEntry), + entry("list", listEntry) + )) + ); - CollectionConfigEntryImpl> listEntry = new CollectionConfigEntryImpl<>((Class>) (Class) List.class, ArrayList::new); - rootEntry.addSubEntry("list", listEntry); - - SimpleConfigEntryImpl booleanEntry = new SimpleConfigEntryImpl<>(Boolean.class); - listEntry.elementEntry(booleanEntry); - - configContainer.attachAndSealTree(rootEntry); + configContainer.attachTree(rootEntry); RegisteredExtensionData readerWriterData = (RegisteredExtensionData) configContainer.entryDataExtensions().get(EntryReaderWriterDefinition.class); readerWriterData.set(rootEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.compoundReaderWriter())); diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCollectionConfigEntry.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCollectionConfigEntry.java index 41f12cf..45b2f78 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCollectionConfigEntry.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCollectionConfigEntry.java @@ -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> exten @SuppressWarnings("rawtypes") TweedConstructFactory 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 elementEntry); } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCompoundConfigEntry.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCompoundConfigEntry.java index 2cc5e03..b8f36f9 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCompoundConfigEntry.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCompoundConfigEntry.java @@ -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 extends CompoundConfigEntry { @SuppressWarnings("rawtypes") TweedConstructFactory 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 { diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CollectionPojoWeaver.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CollectionPojoWeaver.java index faf2a0c..ec67708 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CollectionPojoWeaver.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CollectionPojoWeaver.java @@ -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> 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); } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaver.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaver.java index 010c6f0..633a8a5 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaver.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaver.java @@ -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 compoundEntry = instantiateCompoundEntry(introspector, weavingConfig); + List subEntries = introspector.properties().values().stream() + .filter(this::shouldIncludeCompoundPropertyInWeaving) + .map(property -> weaveCompoundSubEntry(property, newExtensionsData, context)) + .collect(Collectors.toList()); - Map 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 WeavableCompoundConfigEntry instantiateCompoundEntry( PojoClassIntrospector classIntrospector, - CompoundWeavingConfig weavingConfig + CompoundWeavingConfig weavingConfig, + List 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(); } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TrivialPojoWeaver.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TrivialPojoWeaver.java index 2c67cf1..e91c7ec 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TrivialPojoWeaver.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TrivialPojoWeaver.java @@ -12,8 +12,6 @@ public class TrivialPojoWeaver implements TweedPojoWeaver { @Override public ConfigEntry weaveEntry(ActualType valueType, WeavingContext context) { - SimpleConfigEntryImpl entry = new SimpleConfigEntryImpl<>(valueType.declaredType()); - entry.seal(context.configContainer()); - return entry; + return new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType()); } } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingFunction.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingFunction.java index a89abaf..293439e 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingFunction.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingFunction.java @@ -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. */ @Nullable ConfigEntry weaveEntry(ActualType valueType, WeavingContext context); @@ -19,7 +18,7 @@ public interface TweedPojoWeavingFunction { * {@inheritDoc} *
* 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 diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CollectionConfigEntryImpl.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CollectionConfigEntryImpl.java index 5b76ec3..9506d60 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CollectionConfigEntryImpl.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CollectionConfigEntryImpl.java @@ -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> extends BaseConfigEntry implements WeavableCollectionConfigEntry { private final IntFunction constructor; - private @Nullable ConfigEntry elementEntry; + private final @Nullable ConfigEntry elementEntry; - public CollectionConfigEntryImpl(@NotNull Class valueClass, IntFunction constructor) { - super(valueClass); + public CollectionConfigEntryImpl( + ConfigContainer configContainer, + Class valueClass, + IntFunction constructor, + @ConstructParameter(name = "elementEntry") ConfigEntry elementEntry + ) { + super(configContainer, valueClass); this.constructor = constructor; - } - - @Override - public void elementEntry(ConfigEntry elementEntry) { this.elementEntry = elementEntry; } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java index e39b4b7..043ae8e 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java @@ -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 extends BaseConfigEntry implements WeavableCompoundConfigEntry { private final Supplier noArgsConstructor; - private final Map subEntries = new LinkedHashMap<>(); - private final Map> subConfigEntries = new LinkedHashMap<>(); + private final Map subEntries; + private final Map> subConfigEntries; - public StaticPojoCompoundConfigEntry(Class valueClass, Supplier noArgsConstructor) { - super(valueClass); + public StaticPojoCompoundConfigEntry( + ConfigContainer configContainer, + Class valueClass, + Supplier noArgsConstructor, + @ConstructParameter(name = "subEntries") List 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()); } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapper.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapper.java index 68a6e2c..7fe61eb 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapper.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapper.java @@ -82,7 +82,7 @@ public class TweedPojoWeaverBootstrapper { WeavingContext weavingContext = createWeavingContext(); ConfigEntry rootEntry = this.weaveEntry(ActualType.ofClass(pojoClass), weavingContext); - configContainer.attachAndSealTree(rootEntry); + configContainer.attachTree(rootEntry); return configContainer; } @@ -140,9 +140,6 @@ public class TweedPojoWeaverBootstrapper { for (TweedPojoWeaver weaver : weavers) { ConfigEntry configEntry = weaver.weaveEntry(dataClass, context); if (configEntry != null) { - if (!configEntry.sealed()) { - configEntry.seal(configContainer); - } applyPostProcessors(configEntry, context); return configEntry; } diff --git a/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaverTest.java b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaverTest.java index 067fb56..28785d1 100644 --- a/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaverTest.java +++ b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaverTest.java @@ -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)) diff --git a/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapperTest.java b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapperTest.java index 82b43c9..29daff5 100644 --- a/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapperTest.java +++ b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapperTest.java @@ -36,8 +36,6 @@ class TweedPojoWeaverBootstrapperTest { .hasSize(5) )); - configContainer.initialize(); - assertThat(configContainer.extensions()) .satisfiesOnlyOnce(extension -> assertThat(extension).isInstanceOf(DummyExtension.class)) .hasSize(1);