From 03b07591df77538c50ef7dea627803760759836f Mon Sep 17 00:00:00 2001 From: Siphalor Date: Sun, 14 Dec 2025 16:23:00 +0100 Subject: [PATCH] feat(weaver-pojo): Proper API for POJO weaving --- .../testmod/TweedCoatBridgeTestMod.java | 4 +- .../helper/testmod/FabricHelperTestMod.java | 4 +- .../AttributesPojoWeavingProcessorTest.java | 4 +- .../api/PresetsWeavingProcessorTest.java | 4 +- .../ReadWritePojoWeavingProcessorTest.java | 13 +- ...AutoReadWritePojoWeavingProcessorTest.java | 11 +- ...ableReadWritePojoWeavingProcessorTest.java | 7 +- .../ValidatorsPojoWeavingProcessorTest.java | 6 +- .../weaver/pojo/api/TweedPojoWeaver.java | 43 ++++ ...strapper.java => TweedPojoWeaverImpl.java} | 189 ++++++++++++------ .../pojo/impl/weaving/package-info.java | 2 + ...Test.java => TweedPojoWeaverImplTest.java} | 9 +- 12 files changed, 207 insertions(+), 89 deletions(-) create mode 100644 tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/TweedPojoWeaver.java rename tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/{TweedPojoWeaverBootstrapper.java => TweedPojoWeaverImpl.java} (64%) rename tweed5/weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/{TweedPojoWeaverBootstrapperTest.java => TweedPojoWeaverImplTest.java} (86%) diff --git a/tweed5-minecraft/coat-bridge/src/testmod/java/de/siphalor/tweed5/coat/bridge/testmod/TweedCoatBridgeTestMod.java b/tweed5-minecraft/coat-bridge/src/testmod/java/de/siphalor/tweed5/coat/bridge/testmod/TweedCoatBridgeTestMod.java index 7ec7397..b78462b 100644 --- a/tweed5-minecraft/coat-bridge/src/testmod/java/de/siphalor/tweed5/coat/bridge/testmod/TweedCoatBridgeTestMod.java +++ b/tweed5-minecraft/coat-bridge/src/testmod/java/de/siphalor/tweed5/coat/bridge/testmod/TweedCoatBridgeTestMod.java @@ -9,7 +9,7 @@ import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.data.hjson.HjsonSerde; import de.siphalor.tweed5.data.hjson.HjsonWriter; import de.siphalor.tweed5.fabric.helper.api.FabricConfigContainerHelper; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import lombok.CustomLog; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; @@ -35,7 +35,7 @@ public class TweedCoatBridgeTestMod implements ClientModInitializer { @Override public void onInitializeClient() { - configContainer = TweedPojoWeaverBootstrapper.create(TweedCoatBridgeTestModConfig.class).weave(); + configContainer = TweedPojoWeaver.forClass(TweedCoatBridgeTestModConfig.class).weave(); configCoatBridgeExtension = configContainer.extension(TweedCoatBridgeExtension.class) .orElseThrow(() -> new IllegalStateException("TweedCoatBridgeExtension not found")); Arrays.asList( diff --git a/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/FabricHelperTestMod.java b/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/FabricHelperTestMod.java index 2dd6e7a..ca7dbbb 100644 --- a/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/FabricHelperTestMod.java +++ b/tweed5-minecraft/fabric-helper/src/testmod/java/de/siphalor/tweed5/fabric/helper/testmod/FabricHelperTestMod.java @@ -6,7 +6,7 @@ import de.siphalor.tweed5.data.hjson.HjsonSerde; import de.siphalor.tweed5.data.hjson.HjsonWriter; import de.siphalor.tweed5.fabric.helper.api.FabricConfigCommentLoader; import de.siphalor.tweed5.fabric.helper.api.FabricConfigContainerHelper; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import lombok.CustomLog; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; @@ -22,7 +22,7 @@ public class FabricHelperTestMod implements ModInitializer { @Override public void onInitialize() { - configContainer = TweedPojoWeaverBootstrapper.create(TestModConfig.class).weave(); + configContainer = TweedPojoWeaver.forClass(TestModConfig.class).weave(); configFilterExtension = configContainer.extension(AttributesReadWriteFilterExtension.class) .orElseThrow(() -> new IllegalStateException("AttributesReadWriteFilterExtension not found")); configFilterExtension.markAttributeForFiltering("reload"); diff --git a/tweed5/weaver-pojo-attributes-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/attributes/api/AttributesPojoWeavingProcessorTest.java b/tweed5/weaver-pojo-attributes-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/attributes/api/AttributesPojoWeavingProcessorTest.java index 8647d97..2450aa6 100644 --- a/tweed5/weaver-pojo-attributes-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/attributes/api/AttributesPojoWeavingProcessorTest.java +++ b/tweed5/weaver-pojo-attributes-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/attributes/api/AttributesPojoWeavingProcessorTest.java @@ -4,8 +4,8 @@ import de.siphalor.tweed5.attributesextension.api.AttributesExtension; 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 de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.*; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; import org.junit.jupiter.api.Test; import java.util.List; @@ -16,7 +16,7 @@ import static org.assertj.core.api.InstanceOfAssertFactories.type; class AttributesPojoWeavingProcessorTest { @Test void test() { - ConfigContainer configContainer = TweedPojoWeaverBootstrapper.create(Config.class).weave(); + ConfigContainer configContainer = TweedPojoWeaver.forClass(Config.class).weave(); configContainer.initialize(); AttributesExtension attributesExtension = configContainer.extension(AttributesExtension.class).orElseThrow(); diff --git a/tweed5/weaver-pojo-presets-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/presets/api/PresetsWeavingProcessorTest.java b/tweed5/weaver-pojo-presets-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/presets/api/PresetsWeavingProcessorTest.java index ee1dff7..5d6e4c7 100644 --- a/tweed5/weaver-pojo-presets-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/presets/api/PresetsWeavingProcessorTest.java +++ b/tweed5/weaver-pojo-presets-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/presets/api/PresetsWeavingProcessorTest.java @@ -4,8 +4,8 @@ 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 de.siphalor.tweed5.defaultextensions.presets.api.PresetsExtension; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.*; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -20,7 +20,7 @@ class PresetsWeavingProcessorTest { @Test void test() { - ConfigContainer configContainer = TweedPojoWeaverBootstrapper.create(Config.class).weave(); + ConfigContainer configContainer = TweedPojoWeaver.forClass(Config.class).weave(); configContainer.initialize(); PresetsExtension presetsExtension = configContainer.extension(PresetsExtension.class).orElseThrow(); diff --git a/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessorTest.java b/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessorTest.java index e79a799..e18a65b 100644 --- a/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessorTest.java +++ b/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessorTest.java @@ -3,14 +3,18 @@ package de.siphalor.tweed5.weaver.pojoext.serde.api; import com.google.auto.service.AutoService; import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.core.api.entry.ConfigEntry; -import de.siphalor.tweed5.data.extension.api.*; +import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; +import de.siphalor.tweed5.data.extension.api.TweedEntryWriter; +import de.siphalor.tweed5.data.extension.api.TweedReaderWriterProvider; +import de.siphalor.tweed5.data.extension.api.TweedWriteContext; import de.siphalor.tweed5.data.hjson.HjsonLexer; import de.siphalor.tweed5.data.hjson.HjsonReader; import de.siphalor.tweed5.data.hjson.HjsonWriter; import de.siphalor.tweed5.dataapi.api.TweedDataVisitor; import de.siphalor.tweed5.dataapi.api.TweedDataWriteException; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.*; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverImpl; import lombok.*; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; @@ -27,10 +31,7 @@ class ReadWritePojoWeavingProcessorTest { @Test @SneakyThrows void testAnnotated() { - TweedPojoWeaverBootstrapper weaverBootstrapper = TweedPojoWeaverBootstrapper.create( - AnnotatedConfig.class); - - ConfigContainer configContainer = weaverBootstrapper.weave(); + ConfigContainer configContainer = TweedPojoWeaver.forClass(AnnotatedConfig.class).weave(); configContainer.initialize(); AnnotatedConfig config = new AnnotatedConfig(123, "test", new TestClass(987)); diff --git a/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessorTest.java b/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessorTest.java index 5791dae..1bfd13c 100644 --- a/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessorTest.java +++ b/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessorTest.java @@ -9,9 +9,10 @@ import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter; import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; import de.siphalor.tweed5.data.hjson.HjsonWriter; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.*; import de.siphalor.tweed5.weaver.pojo.api.weaving.NullablePojoWeaver; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverImpl; import lombok.Data; import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.Nullable; @@ -33,9 +34,7 @@ class AutoReadWritePojoWeavingProcessorTest { @BeforeEach void setUp() { - var bootstrapper = TweedPojoWeaverBootstrapper.create(AnnotatedConfig.class); - - container = bootstrapper.weave(); + container = TweedPojoWeaver.forClass(AnnotatedConfig.class).weave(); container.initialize(); readWriteExtension = container.extension(ReadWriteExtension.class).orElseThrow(); @@ -77,9 +76,7 @@ class AutoReadWritePojoWeavingProcessorTest { @Test void testUsage() { - var bootstrapper = TweedPojoWeaverBootstrapper.create(AnnotatedConfig.class); - - var container = bootstrapper.weave(); + var container = TweedPojoWeaver.forClass(AnnotatedConfig.class).weave(); container.initialize(); AnnotatedConfig config = new AnnotatedConfig() diff --git a/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessorTest.java b/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessorTest.java index 6664aa5..0884074 100644 --- a/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessorTest.java +++ b/tweed5/weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessorTest.java @@ -8,8 +8,9 @@ import de.siphalor.tweed5.data.extension.api.TweedEntryWriteException; import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; import de.siphalor.tweed5.data.hjson.HjsonWriter; import de.siphalor.tweed5.defaultextensions.pather.api.PatherExtension; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.*; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverImpl; import de.siphalor.tweed5.weaver.pojoext.serde.api.ReadWritePojoWeavingProcessor; import de.siphalor.tweed5.weaver.pojoext.serde.api.auto.AutoReadWritePojoWeavingProcessor; import de.siphalor.tweed5.weaver.pojoext.serde.api.auto.DefaultReadWriteMappings; @@ -34,9 +35,7 @@ class AutoNullableReadWritePojoWeavingProcessorTest { @BeforeEach void setUp() { - var bootstrapper = TweedPojoWeaverBootstrapper.create(AnnotatedConfig.class); - - container = bootstrapper.weave(); + container = TweedPojoWeaver.forClass(AnnotatedConfig.class).weave(); container.initialize(); readWriteExtension = container.extension(ReadWriteExtension.class).orElseThrow(); diff --git a/tweed5/weaver-pojo-validation-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/validation/api/ValidatorsPojoWeavingProcessorTest.java b/tweed5/weaver-pojo-validation-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/validation/api/ValidatorsPojoWeavingProcessorTest.java index 6e38036..19ac1a4 100644 --- a/tweed5/weaver-pojo-validation-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/validation/api/ValidatorsPojoWeavingProcessorTest.java +++ b/tweed5/weaver-pojo-validation-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/validation/api/ValidatorsPojoWeavingProcessorTest.java @@ -3,8 +3,9 @@ package de.siphalor.tweed5.weaver.pojoext.validation.api; import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension; import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssues; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.*; -import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverImpl; import de.siphalor.tweed5.weaver.pojoext.validation.api.validators.WeavableNumberRangeValidator; import lombok.AllArgsConstructor; import lombok.Data; @@ -19,8 +20,7 @@ class ValidatorsPojoWeavingProcessorTest { @ParameterizedTest @CsvSource({"-1,true", "0,false", "50,false", "99,false", "100,true", "101,true"}) void test(int value, boolean issuesExpected) { - var bootstrapper = TweedPojoWeaverBootstrapper.create(Config.class); - ConfigContainer configContainer = bootstrapper.weave(); + ConfigContainer configContainer = TweedPojoWeaver.forClass(Config.class).weave(); configContainer.initialize(); var validationExtension = configContainer.extension(ValidationExtension.class).orElseThrow(); diff --git a/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/TweedPojoWeaver.java b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/TweedPojoWeaver.java new file mode 100644 index 0000000..8a84efc --- /dev/null +++ b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/TweedPojoWeaver.java @@ -0,0 +1,43 @@ +package de.siphalor.tweed5.weaver.pojo.api; + +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.core.api.extension.TweedExtension; +import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverImpl; +import org.jetbrains.annotations.Contract; + +public interface TweedPojoWeaver { + static TweedPojoWeaver forClass(Class pojoClass) { + return TweedPojoWeaverImpl.implForClass(pojoClass); + } + + Class pojoClass(); + ConfigContainer configContainer(); + + @Contract("_ -> this") + default TweedPojoWeaver withWeavingExtensions(Class... weavingExtensions) { + for (Class weavingExtension : weavingExtensions) { + withWeavingExtension(weavingExtension); + } + return this; + } + + @Contract("_ -> this") + TweedPojoWeaver withWeavingExtension(Class weavingExtension); + + @Contract("_ -> this") + default TweedPojoWeaver withExtensions(Class... extensions) { + for (Class extension : extensions) { + withExtension(extension); + } + return this; + } + + @Contract("_ -> this") + TweedPojoWeaver withExtension(Class extension); + + @Contract("_ -> this") + TweedPojoWeaver withConfigContainer(ConfigContainer container); + + ConfigContainer weave(); +} 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/TweedPojoWeaverImpl.java similarity index 64% rename from tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapper.java rename to tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverImpl.java index 778b41c..6ab0b2b 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/TweedPojoWeaverImpl.java @@ -2,10 +2,12 @@ package de.siphalor.tweed5.weaver.pojo.impl.weaving; import de.siphalor.tweed5.annotationinheritance.api.AnnotationInheritanceAwareAnnotatedElement; import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.core.api.container.ConfigContainerSetupPhase; import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.patchwork.api.Patchwork; import de.siphalor.tweed5.patchwork.api.PatchworkFactory; import de.siphalor.tweed5.typeutils.api.type.ActualType; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving; import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension; import de.siphalor.tweed5.weaver.pojo.api.annotation.TweedExtension; @@ -13,6 +15,7 @@ import de.siphalor.tweed5.weaver.pojo.api.weaving.ProtoWeavingContext; import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension; import de.siphalor.tweed5.weaver.pojo.api.weaving.WeavingContext; import lombok.AccessLevel; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.apachecommons.CommonsLog; import org.jspecify.annotations.Nullable; @@ -21,6 +24,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Collectors; /** @@ -29,11 +34,18 @@ import java.util.stream.Collectors; */ @CommonsLog @RequiredArgsConstructor(access = AccessLevel.PRIVATE) -public class TweedPojoWeaverBootstrapper { +public class TweedPojoWeaverImpl implements TweedPojoWeaver { + @Getter private final Class pojoClass; private final AnnotatedElement pojoAnnotations; - private final ConfigContainer configContainer; - private final Collection weavingExtensions; + private final PojoWeaving rootWeavingConfig; + + private final Set> weavingExtensionClasses = new HashSet<>(); + private @Nullable Collection weavingExtensions; + + private @Nullable ConfigContainer configContainer; + private final Set> extensionClasses = new HashSet<>(); + private final WeavingContext.WeavingFn weavingContextFn = new WeavingContext.WeavingFn() { @Override public ConfigEntry weaveEntry( @@ -41,61 +53,21 @@ public class TweedPojoWeaverBootstrapper { Patchwork extensionsData, ProtoWeavingContext context ) { - return TweedPojoWeaverBootstrapper.this.weaveEntry(valueType, extensionsData, context); + return TweedPojoWeaverImpl.this.weaveEntry(valueType, extensionsData, context); } @Override public ConfigEntry weavePseudoEntry(WeavingContext parentContext, String pseudoEntryName, Patchwork extensionsData) { - return TweedPojoWeaverBootstrapper.this.weavePseudoEntry(parentContext, pseudoEntryName, extensionsData); + return TweedPojoWeaverImpl.this.weavePseudoEntry(parentContext, pseudoEntryName, extensionsData); } }; - @Nullable - private PatchworkFactory weavingExtensionsPatchworkFactory; - public static TweedPojoWeaverBootstrapper create(Class pojoClass) { + private @Nullable PatchworkFactory weavingExtensionsPatchworkFactory; + + public static TweedPojoWeaverImpl implForClass(Class pojoClass) { AnnotatedElement pojoAnnotations = new AnnotationInheritanceAwareAnnotatedElement(pojoClass); PojoWeaving rootWeavingConfig = expectAnnotation(pojoAnnotations, PojoWeaving.class); - PojoWeavingExtension[] extensionAnnotations = pojoAnnotations.getAnnotationsByType(PojoWeavingExtension.class); - - //noinspection unchecked - ConfigContainer - configContainer - = (ConfigContainer) createConfigContainer((Class>) rootWeavingConfig.container()); - - TweedExtension[] tweedExtensions = pojoAnnotations.getAnnotationsByType(TweedExtension.class); - //noinspection unchecked - configContainer.registerExtensions( - Arrays.stream(tweedExtensions).map(TweedExtension::value).toArray(Class[]::new) - ); - configContainer.finishExtensionSetup(); - - Collection extensions = loadWeavingExtensions( - Arrays.stream(extensionAnnotations).map(PojoWeavingExtension::value).collect(Collectors.toList()), - configContainer - ); - - return new TweedPojoWeaverBootstrapper<>(pojoClass, pojoAnnotations, configContainer, extensions); - } - - private static Collection loadWeavingExtensions( - Collection> weaverClasses, - ConfigContainer configContainer - ) { - return weaverClasses.stream() - .map(weaverClass -> - TweedPojoWeavingExtension.FACTORY.construct(weaverClass) - .typedArg(ConfigContainer.class, configContainer) - .finish() - ) - .collect(Collectors.toList()); - } - - private static ConfigContainer createConfigContainer(Class> containerClass) { - try { - return ConfigContainer.FACTORY.construct(containerClass).finish(); - } catch (Exception e) { - throw new PojoWeavingException("Failed to instantiate config container"); - } + return new TweedPojoWeaverImpl<>(pojoClass, pojoAnnotations, rootWeavingConfig); } private static A expectAnnotation(AnnotatedElement element, Class annotationClass) { @@ -110,14 +82,84 @@ public class TweedPojoWeaverBootstrapper { } } - public ConfigContainer weave() { - setupWeavingExtensions(); + @Override + public TweedPojoWeaver withConfigContainer(ConfigContainer container) { + if (configContainer != null) { + throw new IllegalStateException("Config container already set"); + } + this.configContainer = container; + return this; + } - assert weavingExtensionsPatchworkFactory != null; + @Override + public ConfigContainer configContainer() { + if (configContainer != null) { + return configContainer; + } + + //noinspection unchecked + this.configContainer + = (ConfigContainer) createConfigContainer((Class>) rootWeavingConfig.container()); + + for (TweedExtension annotation : pojoAnnotations.getAnnotationsByType(TweedExtension.class)) { + extensionClasses.add(annotation.value()); + } + + //noinspection unchecked + configContainer.registerExtensions(extensionClasses.toArray(new Class[0])); + + return configContainer; + } + + private static ConfigContainer createConfigContainer(Class> containerClass) { + try { + return ConfigContainer.FACTORY.construct(containerClass).finish(); + } catch (Exception e) { + throw new PojoWeavingException("Failed to instantiate config container"); + } + } + + @Override + public TweedPojoWeaver withWeavingExtension(Class weavingExtension) { + if (configContainer != null + && configContainer.setupPhase().compareTo(ConfigContainerSetupPhase.TREE_ATTACHED) >= 0) { + throw new IllegalStateException("Cannot add weaving extensions after the tree has been attached"); + } + + weavingExtensionClasses.add(weavingExtension); + + return this; + } + + @Override + public TweedPojoWeaver withExtension(Class extension) { + if (configContainer != null + && configContainer.setupPhase().compareTo(ConfigContainerSetupPhase.EXTENSIONS_SETUP) > 0) { + throw new IllegalStateException("Cannot add extensions after the extensions have been finalized"); + } + + extensionClasses.add(extension); + + if (configContainer != null) { + configContainer.registerExtension(extension); + } + + return this; + } + + public ConfigContainer weave() { + ConfigContainer configContainer = configContainer(); + + if (configContainer.setupPhase().compareTo(ConfigContainerSetupPhase.TREE_ATTACHED) >= 0) { + throw new IllegalStateException("Cannot weave twice"); + } + if (configContainer.setupPhase() == ConfigContainerSetupPhase.EXTENSIONS_SETUP) { + configContainer.finishExtensionSetup(); + } ConfigEntry rootEntry = this.weaveEntry( ActualType.ofClass(pojoClass), - weavingExtensionsPatchworkFactory.create(), + weavingExtensionsPatchworkFactory().create(), ProtoWeavingContext.create(configContainer, pojoAnnotations) ); @@ -128,16 +170,41 @@ public class TweedPojoWeaverBootstrapper { return configContainer; } - private void setupWeavingExtensions() { + private PatchworkFactory weavingExtensionsPatchworkFactory() { + if (weavingExtensionsPatchworkFactory != null) { + return weavingExtensionsPatchworkFactory; + } + PatchworkFactory.Builder weavingExtensionsPatchworkFactoryBuilder = PatchworkFactory.builder(); TweedPojoWeavingExtension.SetupContext setupContext = weavingExtensionsPatchworkFactoryBuilder::registerPart; - for (TweedPojoWeavingExtension weaver : weavingExtensions) { - weaver.setup(setupContext); + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions()) { + weavingExtension.setup(setupContext); } weavingExtensionsPatchworkFactory = weavingExtensionsPatchworkFactoryBuilder.build(); + + return weavingExtensionsPatchworkFactory; + } + + private Collection weavingExtensions() { + if (weavingExtensions != null) { + return weavingExtensions; + } + + for (PojoWeavingExtension annotation : pojoAnnotations.getAnnotationsByType(PojoWeavingExtension.class)) { + weavingExtensionClasses.add(annotation.value()); + } + + weavingExtensions = weavingExtensionClasses.stream() + .map(weavingExtensionClass -> + TweedPojoWeavingExtension.FACTORY.construct(weavingExtensionClass) + .typedArg(ConfigContainer.class, configContainer) + .finish() + ) + .collect(Collectors.toList()); + return weavingExtensions; } private ConfigEntry weaveEntry( @@ -145,6 +212,8 @@ public class TweedPojoWeaverBootstrapper { Patchwork extensionsData, ProtoWeavingContext protoContext ) { + assert configContainer != null && weavingExtensions != null; + extensionsData = extensionsData.copy(); runBeforeWeaveEntryHooks(valueType, extensionsData, protoContext); @@ -191,6 +260,8 @@ public class TweedPojoWeaverBootstrapper { String pseudoEntryName, Patchwork extensionsData ) { + assert configContainer != null && weavingExtensions != null; + extensionsData = extensionsData.copy(); //noinspection unchecked @@ -237,6 +308,8 @@ public class TweedPojoWeaverBootstrapper { Patchwork extensionsData, ProtoWeavingContext protoContext ) { + assert weavingExtensions != null; + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { try { weavingExtension.beforeWeaveEntry(dataClass, extensionsData, protoContext); @@ -251,6 +324,8 @@ public class TweedPojoWeaverBootstrapper { } private void runAfterWeaveEntryHooks(ActualType dataClass, ConfigEntry configEntry, WeavingContext context) { + assert weavingExtensions != null; + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { try { weavingExtension.afterWeaveEntry(dataClass, configEntry, context); @@ -265,6 +340,8 @@ public class TweedPojoWeaverBootstrapper { } private void runAfterWeaveHooks() { + assert weavingExtensions != null; + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { weavingExtension.afterWeave(); } diff --git a/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/package-info.java b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/package-info.java index c299fc3..2871fa7 100644 --- a/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/package-info.java +++ b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/package-info.java @@ -1,4 +1,6 @@ @NullMarked +@ApiStatus.Internal package de.siphalor.tweed5.weaver.pojo.impl.weaving; +import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; 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/TweedPojoWeaverImplTest.java similarity index 86% rename from tweed5/weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverBootstrapperTest.java rename to tweed5/weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/TweedPojoWeaverImplTest.java index 2d03893..7dffcc5 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/TweedPojoWeaverImplTest.java @@ -1,6 +1,7 @@ package de.siphalor.tweed5.weaver.pojo.impl.weaving; import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.weaver.pojo.api.TweedPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.annotation.*; import de.siphalor.tweed5.weaver.pojo.api.weaving.CollectionPojoWeaver; import lombok.Data; @@ -12,11 +13,10 @@ import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.*; import static org.assertj.core.api.Assertions.assertThat; @SuppressWarnings("unused") -class TweedPojoWeaverBootstrapperTest { +class TweedPojoWeaverImplTest { @Test void defaultWeaving() { - TweedPojoWeaverBootstrapper bootstrapper = TweedPojoWeaverBootstrapper.create(MainCompound.class); - ConfigContainer configContainer = bootstrapper.weave(); + ConfigContainer configContainer = TweedPojoWeaver.forClass(MainCompound.class).weave(); assertThat(configContainer.rootEntry()).satisfies(isCompoundEntryForClassWith(MainCompound.class, rootCompound -> assertThat(rootCompound.subEntries()) @@ -39,8 +39,7 @@ class TweedPojoWeaverBootstrapperTest { @Test void weavingWithList() { - TweedPojoWeaverBootstrapper bootstrapper = TweedPojoWeaverBootstrapper.create(CompoundWithList.class); - ConfigContainer configContainer = bootstrapper.weave(); + ConfigContainer configContainer = TweedPojoWeaver.forClass(CompoundWithList.class).weave(); assertThat(configContainer.rootEntry()).satisfies(isCompoundEntryForClassWith(CompoundWithList.class, rootCompound -> assertThat(rootCompound.subEntries())