[*] Remove config entry sealing

This commit is contained in:
2025-06-10 00:46:03 +02:00
parent a6900e673a
commit 2096ae540c
28 changed files with 201 additions and 163 deletions

View File

@@ -50,6 +50,7 @@ dependencies {
testImplementation(libs.mockito)
testAgent(libs.mockito)
testImplementation(libs.assertj)
testImplementation(project(":test-utils"))
}
tasks.compileTestJava {

View File

@@ -1,5 +1,6 @@
rootProject.name = "tweed5"
include("test-utils")
include("tweed5-construct")
include("tweed5-core")
include("tweed5-default-extensions")

View File

@@ -0,0 +1,3 @@
plugins {
java
}

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -3,6 +3,6 @@ package de.siphalor.tweed5.core.api.container;
public enum ConfigContainerSetupPhase {
EXTENSIONS_SETUP,
TREE_SETUP,
TREE_SEALED,
TREE_ATTACHED,
READY,
}

View File

@@ -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!");
}
}
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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(

View File

@@ -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()));

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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());
}

View File

@@ -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;
}

View File

@@ -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))

View File

@@ -36,8 +36,6 @@ class TweedPojoWeaverBootstrapperTest {
.hasSize(5)
));
configContainer.initialize();
assertThat(configContainer.extensions())
.satisfiesOnlyOnce(extension -> assertThat(extension).isInstanceOf(DummyExtension.class))
.hasSize(1);