From dc318722d3b49ad9434d04d9d8cc5bc598c79200 Mon Sep 17 00:00:00 2001 From: Siphalor Date: Sat, 21 Jun 2025 15:27:15 +0200 Subject: [PATCH] [weaver-pojo*] Refactor POJO weaver extensibility --- ...ationInheritanceAwareAnnotatedElement.java | 24 +++ .../impl/AnnotationInheritanceResolver.java | 2 +- ...nInheritanceAwareAnnotatedElementTest.java | 54 +++++++ .../tweed5/patchwork/api/Patchwork.java | 2 - .../api/collection/ClassToInstanceMap.java | 2 +- ...ava => ReadWritePojoWeavingProcessor.java} | 26 +-- .../ReadWritePojoWeavingProcessorTest.java} | 46 ++++-- .../impl/SerdePojoReaderWriterSpecTest.java | 5 +- tweed5-weaver-pojo/build.gradle.kts | 1 + .../annotation/DefaultWeavingExtensions.java | 18 +++ .../pojo/api/annotation/PojoWeaving.java | 12 +- .../api/annotation/PojoWeavingExtension.java | 12 ++ .../api/annotation/PojoWeavingExtensions.java | 12 ++ .../api/weaving/CollectionPojoWeaver.java | 8 +- .../pojo/api/weaving/CompoundPojoWeaver.java | 27 ++-- .../pojo/api/weaving/ProtoWeavingContext.java | 43 +++++ .../pojo/api/weaving/TrivialPojoWeaver.java | 2 +- .../pojo/api/weaving/TweedPojoWeaver.java | 17 -- .../weaving/TweedPojoWeavingExtension.java | 33 ++++ .../pojo/api/weaving/WeavingContext.java | 55 ++----- .../TweedPojoWeavingPostProcessor.java | 12 -- .../api/weaving/postprocess/package-info.java | 4 - .../weaving/TweedPojoWeaverBootstrapper.java | 152 ++++++++++++------ .../api/weaving/CompoundPojoWeaverTest.java | 34 ++-- .../weaver/pojo/api/weaving/package-info.java | 5 + .../TweedPojoWeaverBootstrapperTest.java | 9 +- 26 files changed, 414 insertions(+), 203 deletions(-) rename tweed5-weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/{ReadWritePojoPostProcessor.java => ReadWritePojoWeavingProcessor.java} (87%) rename tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/{WeaverPojoSerdeExtensionTest.java => api/ReadWritePojoWeavingProcessorTest.java} (73%) create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/DefaultWeavingExtensions.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtension.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtensions.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/ProtoWeavingContext.java delete mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeaver.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingExtension.java delete mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/TweedPojoWeavingPostProcessor.java delete mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/package-info.java create mode 100644 tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/package-info.java diff --git a/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElement.java b/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElement.java index 0da2569..a015b97 100644 --- a/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElement.java +++ b/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElement.java @@ -4,12 +4,14 @@ import de.siphalor.tweed5.annotationinheritance.impl.AnnotationInheritanceResolv import de.siphalor.tweed5.typeutils.api.annotations.AnnotationRepeatType; import de.siphalor.tweed5.utils.api.collection.ClassToInstanceMap; import lombok.RequiredArgsConstructor; +import lombok.var; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jspecify.annotations.NonNull; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; @RequiredArgsConstructor public class AnnotationInheritanceAwareAnnotatedElement implements AnnotatedElement { @@ -48,6 +50,28 @@ public class AnnotationInheritanceAwareAnnotatedElement implements AnnotatedElem return getOrResolveAnnotations().get(annotationClass); } + @Override + public @NonNull @NotNull T[] getAnnotationsByType(@NonNull Class annotationClass) { + T annotation = getOrResolveAnnotations().get(annotationClass); + if (annotation != null) { + //noinspection unchecked + T[] array = (T[]) Array.newInstance(annotationClass, 1); + array[0] = annotation; + return array; + } + + AnnotationRepeatType repeatType = AnnotationRepeatType.getType(annotationClass); + if (repeatType instanceof AnnotationRepeatType.Repeatable) { + var containerRepeatType = ((AnnotationRepeatType.Repeatable) repeatType).containerRepeatType(); + Annotation containerAnnotation = getOrResolveAnnotations().get(containerRepeatType.annotationClass()); + if (containerAnnotation != null) { + return containerRepeatType.elements(containerAnnotation); + } + } + //noinspection unchecked + return (T[]) Array.newInstance(annotationClass, 0); + } + @Override public @NotNull Annotation[] getAnnotations() { return getOrResolveAnnotations().values().toArray(new Annotation[0]); diff --git a/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/impl/AnnotationInheritanceResolver.java b/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/impl/AnnotationInheritanceResolver.java index f099bec..e0b2f3b 100644 --- a/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/impl/AnnotationInheritanceResolver.java +++ b/tweed5-annotation-inheritance/src/main/java/de/siphalor/tweed5/annotationinheritance/impl/AnnotationInheritanceResolver.java @@ -27,7 +27,7 @@ public class AnnotationInheritanceResolver { public ClassToInstanceMap resolve() { resolve(main, Collections.emptySet()); - ClassToInstanceMap resolvedAnnotations = new ClassToInstanceMap<>(); + ClassToInstanceMap resolvedAnnotations = ClassToInstanceMap.backedBy(new LinkedHashMap<>()); List aggregatorList = new ArrayList<>(aggregators.values()); for (int i = aggregatorList.size() - 1; i >= 0; i--) { diff --git a/tweed5-annotation-inheritance/src/test/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElementTest.java b/tweed5-annotation-inheritance/src/test/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElementTest.java index ee260c1..3708bf1 100644 --- a/tweed5-annotation-inheritance/src/test/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElementTest.java +++ b/tweed5-annotation-inheritance/src/test/java/de/siphalor/tweed5/annotationinheritance/api/AnnotationInheritanceAwareAnnotatedElementTest.java @@ -18,6 +18,15 @@ class AnnotationInheritanceAwareAnnotatedElementTest { .isEqualTo(1); } + @Test + void getAnnotationsByTypeAForTarget1() { + var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); + assertThat(element.getAnnotationsByType(A.class)) + .singleElement() + .extracting(A::value) + .isEqualTo(1); + } + @Test void getAnnotationBForTarget1() { var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); @@ -27,6 +36,15 @@ class AnnotationInheritanceAwareAnnotatedElementTest { .isEqualTo(2); } + @Test + void getAnnotationsByTypeBForTarget1() { + var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); + assertThat(element.getAnnotationsByType(B.class)) + .singleElement() + .extracting(B::value) + .isEqualTo(2); + } + @Test void getAnnotationCForTarget1() { var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); @@ -36,12 +54,33 @@ class AnnotationInheritanceAwareAnnotatedElementTest { .isEqualTo(10); } + @Test + void getAnnotationsByTypeCForTarget1() { + var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); + assertThat(element.getAnnotationsByType(C.class)) + .singleElement() + .extracting(C::value) + .isEqualTo(10); + } + @Test void getAnnotationRForTarget1() { var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); assertThat(element.getAnnotation(R.class)).isNull(); } + @Test + void getAnnotationsByTypeRForTarget1() { + var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); + assertThat(element.getAnnotationsByType(R.class)) + .satisfiesExactly( + r -> assertThat(r.value()).isEqualTo(4), + r -> assertThat(r.value()).isEqualTo(2), + r -> assertThat(r.value()).isEqualTo(3), + r -> assertThat(r.value()).isEqualTo(10) + ); + } + @Test void getAnnotationRsForTarget1() { var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); @@ -58,6 +97,21 @@ class AnnotationInheritanceAwareAnnotatedElementTest { ); } + @Test + void getAnnotationsByTypeRsForTarget1() { + var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); + assertThat(element.getAnnotationsByType(Rs.class)) + .singleElement() + .extracting(Rs::value) + .asInstanceOf(array(R[].class)) + .satisfiesExactly( + r -> assertThat(r.value()).isEqualTo(4), + r -> assertThat(r.value()).isEqualTo(2), + r -> assertThat(r.value()).isEqualTo(3), + r -> assertThat(r.value()).isEqualTo(10) + ); + } + @Test void getAnnotationsForTarget1() { var element = new AnnotationInheritanceAwareAnnotatedElement(Target1.class); diff --git a/tweed5-patchwork/src/main/java/de/siphalor/tweed5/patchwork/api/Patchwork.java b/tweed5-patchwork/src/main/java/de/siphalor/tweed5/patchwork/api/Patchwork.java index 55c2b30..87b9ba5 100644 --- a/tweed5-patchwork/src/main/java/de/siphalor/tweed5/patchwork/api/Patchwork.java +++ b/tweed5-patchwork/src/main/java/de/siphalor/tweed5/patchwork/api/Patchwork.java @@ -1,6 +1,5 @@ package de.siphalor.tweed5.patchwork.api; -import org.jetbrains.annotations.CheckReturnValue; import org.jetbrains.annotations.Contract; import org.jspecify.annotations.Nullable; @@ -10,7 +9,6 @@ public interface Patchwork { @Contract(mutates = "this") void set(PatchworkPartAccess access, T value) throws InvalidPatchworkAccessException; - @CheckReturnValue @Contract(pure = true) Patchwork copy(); } diff --git a/tweed5-utils/src/main/java/de/siphalor/tweed5/utils/api/collection/ClassToInstanceMap.java b/tweed5-utils/src/main/java/de/siphalor/tweed5/utils/api/collection/ClassToInstanceMap.java index ca6426a..af38524 100644 --- a/tweed5-utils/src/main/java/de/siphalor/tweed5/utils/api/collection/ClassToInstanceMap.java +++ b/tweed5-utils/src/main/java/de/siphalor/tweed5/utils/api/collection/ClassToInstanceMap.java @@ -38,7 +38,7 @@ public class ClassToInstanceMap implements Iterable V get(Class key) { + public @Nullable V get(Class key) { return (V) delegate.get(key); } diff --git a/tweed5-weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoPostProcessor.java b/tweed5-weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessor.java similarity index 87% rename from tweed5-weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoPostProcessor.java rename to tweed5-weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessor.java index 17c5afa..e6efb6d 100644 --- a/tweed5-weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoPostProcessor.java +++ b/tweed5-weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessor.java @@ -1,13 +1,15 @@ package de.siphalor.tweed5.weaver.pojoext.serde.api; +import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.core.api.entry.ConfigEntry; 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.TweedReaderWriterProvider; import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; +import de.siphalor.tweed5.typeutils.api.type.ActualType; +import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension; import de.siphalor.tweed5.weaver.pojo.api.weaving.WeavingContext; -import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor; import de.siphalor.tweed5.weaver.pojoext.serde.impl.SerdePojoReaderWriterSpec; import lombok.extern.apachecommons.CommonsLog; import org.jspecify.annotations.Nullable; @@ -18,11 +20,21 @@ import java.lang.reflect.InvocationTargetException; import java.util.*; @CommonsLog -public class ReadWritePojoPostProcessor implements TweedPojoWeavingPostProcessor { +public class ReadWritePojoWeavingProcessor implements TweedPojoWeavingExtension { private final Map>> readerFactories = new HashMap<>(); private final Map>> writerFactories = new HashMap<>(); + private final ReadWriteExtension readWriteExtension; - public ReadWritePojoPostProcessor() { + public ReadWritePojoWeavingProcessor(ConfigContainer configContainer) { + this.readWriteExtension = configContainer.extension(ReadWriteExtension.class) + .orElseThrow(() -> new IllegalStateException( + "You must register a " + ReadWriteExtension.class.getSimpleName() + + " to use the " + getClass().getSimpleName() + )); + } + + @Override + public void setup(SetupContext context) { loadProviders(); } @@ -63,18 +75,12 @@ public class ReadWritePojoPostProcessor implements TweedPojoWeavingPostProcessor } @Override - public void apply(ConfigEntry configEntry, WeavingContext context) { + public void afterWeaveEntry(ActualType valueType, ConfigEntry configEntry, WeavingContext context) { EntryReadWriteConfig entryConfig = context.annotations().getAnnotation(EntryReadWriteConfig.class); if (entryConfig == null) { return; } - ReadWriteExtension readWriteExtension = context.configContainer().extension(ReadWriteExtension.class).orElse(null); - if (readWriteExtension == null && log.isErrorEnabled()) { - log.error("You must not use " + getClass().getName() + " without the " + ReadWriteExtension.class.getName()); - return; - } - //noinspection rawtypes,unchecked readWriteExtension.setEntryReaderWriter( (ConfigEntry) configEntry, diff --git a/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/WeaverPojoSerdeExtensionTest.java b/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessorTest.java similarity index 73% rename from tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/WeaverPojoSerdeExtensionTest.java rename to tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessorTest.java index 4a26fa7..b00999d 100644 --- a/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/WeaverPojoSerdeExtensionTest.java +++ b/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/api/ReadWritePojoWeavingProcessorTest.java @@ -1,4 +1,4 @@ -package de.siphalor.tweed5.weaver.pojoext.serde; +package de.siphalor.tweed5.weaver.pojoext.serde.api; import com.google.auto.service.AutoService; import de.siphalor.tweed5.core.api.container.ConfigContainer; @@ -10,11 +10,12 @@ 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.annotation.CompoundWeaving; +import de.siphalor.tweed5.weaver.pojo.api.annotation.DefaultWeavingExtensions; import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving; +import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension; import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; -import de.siphalor.tweed5.weaver.pojoext.serde.api.EntryReadWriteConfig; -import de.siphalor.tweed5.weaver.pojoext.serde.api.ReadWritePojoPostProcessor; import lombok.*; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import java.io.StringReader; @@ -22,12 +23,13 @@ import java.io.StringWriter; import static org.assertj.core.api.Assertions.assertThat; -class WeaverPojoSerdeExtensionTest { +class ReadWritePojoWeavingProcessorTest { @Test @SneakyThrows void testAnnotated() { - TweedPojoWeaverBootstrapper weaverBootstrapper = TweedPojoWeaverBootstrapper.create(AnnotatedConfig.class); + TweedPojoWeaverBootstrapper weaverBootstrapper = TweedPojoWeaverBootstrapper.create( + AnnotatedConfig.class); ConfigContainer configContainer = weaverBootstrapper.weave(); configContainer.initialize(); @@ -38,12 +40,27 @@ class WeaverPojoSerdeExtensionTest { StringWriter stringWriter = new StringWriter(); HjsonWriter hjsonWriter = new HjsonWriter(stringWriter, new HjsonWriter.Options()); - readWriteExtension.write(hjsonWriter, config, configContainer.rootEntry(), readWriteExtension.createReadWriteContextExtensionsData()); + readWriteExtension.write( + hjsonWriter, + config, + configContainer.rootEntry(), + readWriteExtension.createReadWriteContextExtensionsData() + ); - assertThat(stringWriter).hasToString("{\n\tanInt: 123\n\ttext: test\n\ttest: my cool custom writer\n}\n"); + assertThat(stringWriter).hasToString(""" + { + \tanInt: 123 + \ttext: test + \ttest: my cool custom writer + } + """); - HjsonReader reader = new HjsonReader(new HjsonLexer(new StringReader( - "{\n\tanInt: 987\n\ttext: abdef\n\ttest: { inner: 29 }\n}" + HjsonReader reader = new HjsonReader(new HjsonLexer(new StringReader(""" + { + \tanInt: 987 + \ttext: abdef + \ttest: { inner: 29 } + }""" ))); assertThat(readWriteExtension.read( reader, @@ -59,10 +76,10 @@ class WeaverPojoSerdeExtensionTest { context.registerWriterFactory("tweed5.test.dummy", delegates -> new TweedEntryWriter>() { @Override public void write( - @NonNull TweedDataVisitor writer, - Object value, + TweedDataVisitor writer, + @Nullable Object value, ConfigEntry entry, - @NonNull TweedWriteContext context + TweedWriteContext context ) throws TweedDataWriteException { writer.visitString("my cool custom writer"); } @@ -70,9 +87,12 @@ class WeaverPojoSerdeExtensionTest { } } - @PojoWeaving(extensions = ReadWriteExtension.class, postProcessors = ReadWritePojoPostProcessor.class) + @PojoWeaving(extensions = ReadWriteExtension.class) + @DefaultWeavingExtensions + @PojoWeavingExtension(de.siphalor.tweed5.weaver.pojoext.serde.api.ReadWritePojoWeavingProcessor.class) @CompoundWeaving @EntryReadWriteConfig("tweed5.compound") + // lombok @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode diff --git a/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/impl/SerdePojoReaderWriterSpecTest.java b/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/impl/SerdePojoReaderWriterSpecTest.java index 5a2598a..8f6aa0b 100644 --- a/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/impl/SerdePojoReaderWriterSpecTest.java +++ b/tweed5-weaver-pojo-serde-extension/src/test/java/de/siphalor/tweed5/weaver/pojoext/serde/impl/SerdePojoReaderWriterSpecTest.java @@ -1,6 +1,7 @@ package de.siphalor.tweed5.weaver.pojoext.serde.impl; import lombok.SneakyThrows; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -52,7 +53,7 @@ class SerdePojoReaderWriterSpecTest { "abc..def;5;.", }) @SneakyThrows - void parseError(String input, int index, String codePoint) { + void parseError(String input, int index, @Nullable String codePoint) { assertThatThrownBy(() -> SerdePojoReaderWriterSpec.parse(input)) .asInstanceOf(type(SerdePojoReaderWriterSpec.ParseException.class)) .isInstanceOf(SerdePojoReaderWriterSpec.ParseException.class) @@ -62,4 +63,4 @@ class SerdePojoReaderWriterSpecTest { .isEqualTo(codePoint == null ? -1 : codePoint.codePointAt(0)) ); } -} \ No newline at end of file +} diff --git a/tweed5-weaver-pojo/build.gradle.kts b/tweed5-weaver-pojo/build.gradle.kts index 80abcd9..3e9cc1d 100644 --- a/tweed5-weaver-pojo/build.gradle.kts +++ b/tweed5-weaver-pojo/build.gradle.kts @@ -4,6 +4,7 @@ plugins { dependencies { implementation(project(":tweed5-construct")) + api(project(":tweed5-annotation-inheritance")) api(project(":tweed5-core")) api(project(":tweed5-naming-format")) api(project(":tweed5-type-utils")) diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/DefaultWeavingExtensions.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/DefaultWeavingExtensions.java new file mode 100644 index 0000000..6c8dc23 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/DefaultWeavingExtensions.java @@ -0,0 +1,18 @@ +package de.siphalor.tweed5.weaver.pojo.api.annotation; + +import de.siphalor.tweed5.annotationinheritance.api.AnnotationInheritance; +import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver; +import de.siphalor.tweed5.weaver.pojo.api.weaving.TrivialPojoWeaver; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@AnnotationInheritance(passOn = PojoWeavingExtension.class) +@PojoWeavingExtension(CompoundPojoWeaver.class) +@PojoWeavingExtension(TrivialPojoWeaver.class) +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface DefaultWeavingExtensions { +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeaving.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeaving.java index 1c7c1bc..e8f00aa 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeaving.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeaving.java @@ -5,8 +5,7 @@ import de.siphalor.tweed5.core.api.extension.TweedExtension; import de.siphalor.tweed5.core.impl.DefaultConfigContainer; import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver; import de.siphalor.tweed5.weaver.pojo.api.weaving.TrivialPojoWeaver; -import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeaver; -import de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor; +import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -14,16 +13,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) public @interface PojoWeaving { Class container() default DefaultConfigContainer.class; - Class[] weavers() default { - CompoundPojoWeaver.class, - TrivialPojoWeaver.class, - }; - - Class[] postProcessors() default {}; - Class[] extensions() default {}; } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtension.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtension.java new file mode 100644 index 0000000..04ff4dd --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtension.java @@ -0,0 +1,12 @@ +package de.siphalor.tweed5.weaver.pojo.api.annotation; + +import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeavingExtension; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Repeatable(PojoWeavingExtensions.class) +public @interface PojoWeavingExtension { + Class value(); +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtensions.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtensions.java new file mode 100644 index 0000000..9000797 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/PojoWeavingExtensions.java @@ -0,0 +1,12 @@ +package de.siphalor.tweed5.weaver.pojo.api.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface PojoWeavingExtensions { + PojoWeavingExtension[] value(); +} 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 c2b87ec..6277dc6 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 @@ -20,7 +20,7 @@ import java.lang.reflect.AnnotatedElement; import java.util.*; import java.util.function.IntFunction; -public class CollectionPojoWeaver implements TweedPojoWeaver { +public class CollectionPojoWeaver implements TweedPojoWeavingExtension { private static final CollectionWeavingConfig DEFAULT_WEAVING_CONFIG = CollectionWeavingConfigImpl.builder() .collectionEntryClass(CollectionConfigEntryImpl.class) .build(); @@ -51,10 +51,8 @@ public class CollectionPojoWeaver implements TweedPojoWeaver { ConfigEntry elementEntry = context.weaveEntry( collectionTypeParams.get(0), - context.subContextBuilder("element") - .annotations(collectionTypeParams.get(0)) - .extensionsData(newExtensionsData) - .build() + newExtensionsData, + ProtoWeavingContext.subContextFor(context, "element", collectionTypeParams.get(0)) ); return WeavableCollectionConfigEntry.FACTORY 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 e55759e..6588540 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 @@ -29,7 +29,7 @@ import java.util.stream.Collectors; /** * A weaver that weaves classes with the {@link CompoundWeaving} annotation as compound entries. */ -public class CompoundPojoWeaver implements TweedPojoWeaver { +public class CompoundPojoWeaver implements TweedPojoWeavingExtension { private static final CompoundWeavingConfig DEFAULT_WEAVING_CONFIG = CompoundWeavingConfigImpl.builder() .compoundSourceNamingFormat(NamingFormats.camelCase()) .compoundTargetNamingFormat(NamingFormats.camelCase()) @@ -147,21 +147,28 @@ public class CompoundPojoWeaver implements TweedPojoWeaver { private WeavableCompoundConfigEntry.SubEntry weaveCompoundSubEntry( PojoClassIntrospector.Property property, Patchwork newExtensionsData, - WeavingContext parentContext + WeavingContext context ) { assert weavingConfigAccess != null; CompoundWeavingConfig weavingConfig = newExtensionsData.get(weavingConfigAccess); assert weavingConfig != null; String name = convertName(property.field().getName(), weavingConfig); - WeavingContext subContext = createSubContextForProperty(property, name, newExtensionsData, parentContext); ConfigEntry subEntry; if (property.isFinal()) { // TODO throw new UnsupportedOperationException("Final config entries are not supported in weaving yet."); } else { - subEntry = subContext.weaveEntry(ActualType.ofUsedType(property.field().getAnnotatedType()), subContext); + subEntry = context.weaveEntry( + ActualType.ofUsedType(property.field().getAnnotatedType()), + newExtensionsData, + ProtoWeavingContext.subContextFor( + context, + property.field().getName(), + collectAnnotationsForField(property.field()) + ) + ); } return new StaticPojoCompoundConfigEntry.SubEntry( @@ -193,18 +200,6 @@ public class CompoundPojoWeaver implements TweedPojoWeaver { return namingFormat; } - private WeavingContext createSubContextForProperty( - PojoClassIntrospector.Property property, - String name, - Patchwork newExtensionsData, - WeavingContext parentContext - ) { - return parentContext.subContextBuilder(name) - .annotations(collectAnnotationsForField(property.field())) - .extensionsData(newExtensionsData) - .build(); - } - private AnnotatedElement collectAnnotationsForField(Field field) { LayeredTypeAnnotations annotations = new LayeredTypeAnnotations(); annotations.appendLayerFrom(TypeAnnotationLayer.TYPE_DECLARATION, field.getType()); diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/ProtoWeavingContext.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/ProtoWeavingContext.java new file mode 100644 index 0000000..22c48c6 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/ProtoWeavingContext.java @@ -0,0 +1,43 @@ +package de.siphalor.tweed5.weaver.pojo.api.weaving; + +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import lombok.Value; +import org.jspecify.annotations.Nullable; + +import java.lang.reflect.AnnotatedElement; +import java.util.Arrays; + +@Value +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class ProtoWeavingContext { + @Nullable WeavingContext parent; + ConfigContainer configContainer; + String[] path; + AnnotatedElement annotations; + + public static ProtoWeavingContext subContextFor( + WeavingContext weavingContext, + String subPathName, + AnnotatedElement annotations + ) { + String[] path = Arrays.copyOf(weavingContext.path(), weavingContext.path().length + 1, String[].class); + path[path.length - 1] = subPathName; + return new ProtoWeavingContext( + weavingContext, + weavingContext.configContainer(), + path, + annotations + ); + } + + public static ProtoWeavingContext create(ConfigContainer configContainer, AnnotatedElement annotations) { + return new ProtoWeavingContext( + null, + configContainer, + new String[0], + annotations + ); + } +} 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 e91c7ec..d8883b4 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 @@ -4,7 +4,7 @@ import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl; import de.siphalor.tweed5.typeutils.api.type.ActualType; -public class TrivialPojoWeaver implements TweedPojoWeaver { +public class TrivialPojoWeaver implements TweedPojoWeavingExtension { @Override public void setup(SetupContext context) { // nothing to set up here diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeaver.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeaver.java deleted file mode 100644 index ccee792..0000000 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeaver.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.siphalor.tweed5.weaver.pojo.api.weaving; - -import de.siphalor.tweed5.construct.api.TweedConstructFactory; -import de.siphalor.tweed5.patchwork.api.PatchworkFactory; -import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; -import org.jetbrains.annotations.ApiStatus; - -public interface TweedPojoWeaver extends TweedPojoWeavingFunction { - TweedConstructFactory FACTORY = TweedConstructFactory.builder(TweedPojoWeaver.class).build(); - - @ApiStatus.OverrideOnly - void setup(SetupContext context); - - interface SetupContext { - PatchworkPartAccess registerWeavingContextExtensionData(Class dataClass); - } -} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingExtension.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingExtension.java new file mode 100644 index 0000000..17e2298 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/TweedPojoWeavingExtension.java @@ -0,0 +1,33 @@ +package de.siphalor.tweed5.weaver.pojo.api.weaving; + +import de.siphalor.tweed5.construct.api.TweedConstructFactory; +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.patchwork.api.Patchwork; +import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; +import de.siphalor.tweed5.typeutils.api.type.ActualType; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.Nullable; + +public interface TweedPojoWeavingExtension extends TweedPojoWeavingFunction { + TweedConstructFactory FACTORY = + TweedConstructFactory.builder(TweedPojoWeavingExtension.class) + .typedArg(ConfigContainer.class) + .build(); + + @ApiStatus.OverrideOnly + void setup(SetupContext context); + + default void beforeWeaveEntry(ActualType valueType, Patchwork extensionsData, ProtoWeavingContext context) {} + + @Override + default @Nullable ConfigEntry weaveEntry(ActualType valueType, WeavingContext context) { + return null; + } + + default void afterWeaveEntry(ActualType valueType, ConfigEntry configEntry, WeavingContext context) {} + + interface SetupContext { + PatchworkPartAccess registerWeavingContextExtensionData(Class dataClass); + } +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/WeavingContext.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/WeavingContext.java index 58ddecb..6a8d30b 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/WeavingContext.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/WeavingContext.java @@ -4,63 +4,30 @@ import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.patchwork.api.Patchwork; import de.siphalor.tweed5.typeutils.api.type.ActualType; -import lombok.*; -import lombok.experimental.Accessors; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.Value; import org.jspecify.annotations.Nullable; import java.lang.reflect.AnnotatedElement; -import java.util.Arrays; @Value -public class WeavingContext implements TweedPojoWeavingFunction.NonNull { +@Builder +public class WeavingContext { @Nullable WeavingContext parent; @Getter(AccessLevel.NONE) - TweedPojoWeavingFunction.NonNull weavingFunction; + WeavingFn weavingFunction; ConfigContainer configContainer; String[] path; Patchwork extensionsData; AnnotatedElement annotations; - public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer configContainer) { - return new Builder(null, weavingFunction, configContainer, new String[0]); + public ConfigEntry weaveEntry(ActualType valueType, Patchwork extensionsData, ProtoWeavingContext context) { + return weavingFunction.weaveEntry(valueType, extensionsData, context); } - public static Builder builder(TweedPojoWeavingFunction.NonNull weavingFunction, ConfigContainer configContainer, String baseName) { - return new Builder(null, weavingFunction, configContainer, new String[]{ baseName }); - } - - public Builder subContextBuilder(String subPathName) { - String[] newPath = Arrays.copyOf(path, path.length + 1); - newPath[path.length] = subPathName; - return new Builder(this, weavingFunction, configContainer, newPath).extensionsData(extensionsData); - } - - @Override - public ConfigEntry weaveEntry(ActualType valueType, WeavingContext context) { - return weavingFunction.weaveEntry(valueType, context); - } - - @Accessors(fluent = true, chain = true) - @Setter - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - public static class Builder { - @Nullable - private final WeavingContext parent; - private final TweedPojoWeavingFunction.NonNull weavingFunction; - private final ConfigContainer configContainer; - private final String[] path; - private Patchwork extensionsData; - private AnnotatedElement annotations; - - public WeavingContext build() { - return new WeavingContext( - parent, - weavingFunction, - configContainer, - path, - extensionsData, - annotations - ); - } + public interface WeavingFn { + ConfigEntry weaveEntry(ActualType valueType, Patchwork extensionsData, ProtoWeavingContext context); } } diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/TweedPojoWeavingPostProcessor.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/TweedPojoWeavingPostProcessor.java deleted file mode 100644 index 65e9f25..0000000 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/TweedPojoWeavingPostProcessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess; - -import de.siphalor.tweed5.construct.api.TweedConstructFactory; -import de.siphalor.tweed5.core.api.entry.ConfigEntry; -import de.siphalor.tweed5.weaver.pojo.api.weaving.WeavingContext; - -public interface TweedPojoWeavingPostProcessor { - TweedConstructFactory FACTORY = - TweedConstructFactory.builder(TweedPojoWeavingPostProcessor.class).build(); - - void apply(ConfigEntry configEntry, WeavingContext context); -} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/package-info.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/package-info.java deleted file mode 100644 index 6f55af1..0000000 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/postprocess/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -@NullMarked -package de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess; - -import org.jspecify.annotations.NullMarked; 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 04c2d6a..4ea058d 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 @@ -1,20 +1,23 @@ 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.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.annotation.PojoWeaving; -import de.siphalor.tweed5.weaver.pojo.api.weaving.TweedPojoWeaver; +import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension; +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 de.siphalor.tweed5.weaver.pojo.api.weaving.postprocess.TweedPojoWeavingPostProcessor; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.extern.apachecommons.CommonsLog; import org.jspecify.annotations.Nullable; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.util.Arrays; import java.util.Collection; import java.util.stream.Collectors; @@ -27,36 +30,43 @@ import java.util.stream.Collectors; @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public class TweedPojoWeaverBootstrapper { private final Class pojoClass; + private final AnnotatedElement pojoAnnotations; private final ConfigContainer configContainer; - private final Collection weavers; - private final Collection postProcessors; + private final Collection weavingExtensions; @Nullable - private PatchworkFactory contextExtensionsPatchworkFactory; + private PatchworkFactory weavingExtensionsPatchworkFactory; public static TweedPojoWeaverBootstrapper create(Class pojoClass) { - PojoWeaving rootWeavingConfig = expectAnnotation(pojoClass, PojoWeaving.class); + 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()); - - Collection weavers = loadWeavers(Arrays.asList(rootWeavingConfig.weavers())); - Collection postProcessors = loadPostProcessors(Arrays.asList(rootWeavingConfig.postProcessors())); + ConfigContainer + configContainer + = (ConfigContainer) createConfigContainer((Class>) rootWeavingConfig.container()); configContainer.registerExtensions(rootWeavingConfig.extensions()); configContainer.finishExtensionSetup(); - return new TweedPojoWeaverBootstrapper<>(pojoClass, configContainer, weavers, postProcessors); + Collection extensions = loadWeavingExtensions( + Arrays.stream(extensionAnnotations).map(PojoWeavingExtension::value).collect(Collectors.toList()), + configContainer + ); + + return new TweedPojoWeaverBootstrapper<>(pojoClass, pojoAnnotations, configContainer, extensions); } - private static Collection loadWeavers(Collection> weaverClasses) { + private static Collection loadWeavingExtensions( + Collection> weaverClasses, + ConfigContainer configContainer + ) { return weaverClasses.stream() - .map(weaverClass -> TweedPojoWeaver.FACTORY.construct(weaverClass).finish()) - .collect(Collectors.toList()); - } - - private static Collection loadPostProcessors(Collection> postProcessorClasses) { - return postProcessorClasses.stream() - .map(postProcessorClass -> TweedPojoWeavingPostProcessor.FACTORY.construct(postProcessorClass).finish()) + .map(weaverClass -> + TweedPojoWeavingExtension.FACTORY.construct(weaverClass) + .typedArg(ConfigContainer.class, configContainer) + .finish() + ) .collect(Collectors.toList()); } @@ -68,69 +78,107 @@ public class TweedPojoWeaverBootstrapper { } } - private static A expectAnnotation(Class clazz, Class annotationClass) { - A annotation = clazz.getAnnotation(annotationClass); + private static A expectAnnotation(AnnotatedElement element, Class annotationClass) { + A annotation = element.getAnnotation(annotationClass); if (annotation == null) { - throw new PojoWeavingException("Annotation " + annotationClass.getName() + " must be defined on class " + clazz); + throw new PojoWeavingException("Annotation " + + annotationClass.getName() + + " must be defined on class " + + element); } else { return annotation; } } public ConfigContainer weave() { - setupWeavers(); - WeavingContext weavingContext = createWeavingContext(); + setupWeavingExtensions(); + + assert weavingExtensionsPatchworkFactory != null; + + ConfigEntry rootEntry = this.weaveEntry( + ActualType.ofClass(pojoClass), + weavingExtensionsPatchworkFactory.create(), + ProtoWeavingContext.create(configContainer, pojoAnnotations) + ); - ConfigEntry rootEntry = this.weaveEntry(ActualType.ofClass(pojoClass), weavingContext); configContainer.attachTree(rootEntry); return configContainer; } - private void setupWeavers() { - PatchworkFactory.Builder contextExtensionsPatchworkFactoryBuilder = PatchworkFactory.builder(); + private void setupWeavingExtensions() { + PatchworkFactory.Builder weavingExtensionsPatchworkFactoryBuilder = PatchworkFactory.builder(); - TweedPojoWeaver.SetupContext setupContext = contextExtensionsPatchworkFactoryBuilder::registerPart; + TweedPojoWeavingExtension.SetupContext setupContext = weavingExtensionsPatchworkFactoryBuilder::registerPart; - for (TweedPojoWeaver weaver : weavers) { + for (TweedPojoWeavingExtension weaver : weavingExtensions) { weaver.setup(setupContext); } - contextExtensionsPatchworkFactory = contextExtensionsPatchworkFactoryBuilder.build(); + weavingExtensionsPatchworkFactory = weavingExtensionsPatchworkFactoryBuilder.build(); } - private WeavingContext createWeavingContext() { - try { - assert contextExtensionsPatchworkFactory != null; - Patchwork extensionsData = contextExtensionsPatchworkFactory.create(); + private ConfigEntry weaveEntry( + ActualType dataClass, + Patchwork extensionsData, + ProtoWeavingContext protoContext + ) { + extensionsData = extensionsData.copy(); - return WeavingContext.builder(this::weaveEntry, configContainer) - .extensionsData(extensionsData) - .annotations(pojoClass) - .build(); - } catch (Throwable e) { - throw new PojoWeavingException("Failed to create weaving context's extension data"); - } - } + runBeforeWeaveHooks(dataClass, extensionsData, protoContext); - private ConfigEntry weaveEntry(ActualType dataClass, WeavingContext context) { - for (TweedPojoWeaver weaver : weavers) { - ConfigEntry configEntry = weaver.weaveEntry(dataClass, context); - if (configEntry != null) { - applyPostProcessors(configEntry, context); - return configEntry; + WeavingContext context = WeavingContext.builder() + .parent(protoContext.parent()) + .weavingFunction(this::weaveEntry) + .configContainer(configContainer) + .path(protoContext.path()) + .extensionsData(extensionsData) + .annotations(new AnnotationInheritanceAwareAnnotatedElement(protoContext.annotations())) + .build(); + + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { + try { + ConfigEntry configEntry = weavingExtension.weaveEntry(dataClass, context); + if (configEntry != null) { + runAfterWeaveHooks(dataClass, configEntry, context); + return configEntry; + } + } catch (Exception e) { + log.error("Failed to run Tweed POJO weaver (" + weavingExtension.getClass().getName() + ")", e); } } throw new PojoWeavingException("Failed to weave " + dataClass + ": No matching weavers found"); } - private void applyPostProcessors(ConfigEntry configEntry, WeavingContext context) { - for (TweedPojoWeavingPostProcessor postProcessor : postProcessors) { + private void runBeforeWeaveHooks( + ActualType dataClass, + Patchwork extensionsData, + ProtoWeavingContext protoContext + ) { + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { try { - postProcessor.apply(configEntry, context); + weavingExtension.beforeWeaveEntry(dataClass, extensionsData, protoContext); } catch (Exception e) { - log.error("Failed to apply Tweed POJO weaver post processor", e); + log.error( + "Failed to apply Tweed POJO weaver before weave hook (" + + weavingExtension.getClass().getName() + ")", + e + ); + } + } + } + + private void runAfterWeaveHooks(ActualType dataClass, ConfigEntry configEntry, WeavingContext context) { + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { + try { + weavingExtension.afterWeaveEntry(dataClass, configEntry, context); + } catch (Exception e) { + log.error( + "Failed to apply Tweed POJO weaver after weave hook (" + + weavingExtension.getClass().getName() + ")", + e + ); } } } 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 6c66251..bcd61cf 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 @@ -3,13 +3,15 @@ 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.impl.entry.SimpleConfigEntryImpl; +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.annotation.CompoundWeaving; -import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.NullUnmarked; import org.junit.jupiter.api.Test; +import java.util.Objects; + import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isCompoundEntryForClassWith; import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isSimpleEntryForClass; import static org.assertj.core.api.Assertions.assertThat; @@ -28,19 +30,31 @@ class CompoundPojoWeaverTest { PatchworkFactory weavingContextExtensionDataFactory = weavingContextExtensionDataFactoryBuilder.build(); - WeavingContext weavingContext = WeavingContext.builder(new TweedPojoWeavingFunction.NonNull() { + WeavingContext weavingContext = WeavingContext.builder() + .weavingFunction(new WeavingContext.WeavingFn() { @Override - public @NotNull ConfigEntry weaveEntry(ActualType valueType, WeavingContext context) { - ConfigEntry entry = compoundWeaver.weaveEntry(valueType, context); - if (entry != null) { - return entry; - } else { - return new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType()); - } + public ConfigEntry weaveEntry( + ActualType valueType, + Patchwork extensionsData, + ProtoWeavingContext protoContext + ) { + WeavingContext context = WeavingContext.builder() + .configContainer(protoContext.configContainer()) + .annotations(valueType) + .extensionsData(extensionsData.copy()) + .path(protoContext.path()) + .weavingFunction(protoContext.parent()::weaveEntry) + .build(); + return Objects.requireNonNullElseGet( + compoundWeaver.weaveEntry(valueType, context), + () -> new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType()) + ); } - }, mock(ConfigContainer.class)) + }) .extensionsData(weavingContextExtensionDataFactory.create()) .annotations(Compound.class) + .path(new String[0]) + .configContainer(mock(ConfigContainer.class)) .build(); ConfigEntry resultEntry = compoundWeaver.weaveEntry(ActualType.ofClass(Compound.class), weavingContext); diff --git a/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/package-info.java b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/package-info.java new file mode 100644 index 0000000..2b37106 --- /dev/null +++ b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/api/weaving/package-info.java @@ -0,0 +1,5 @@ +@NullMarked + +package de.siphalor.tweed5.weaver.pojo.api.weaving; + +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/TweedPojoWeaverBootstrapperTest.java index 29daff5..8c3b1a7 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 @@ -4,10 +4,10 @@ import com.google.auto.service.AutoService; import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.core.api.extension.TweedExtension; import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving; +import de.siphalor.tweed5.weaver.pojo.api.annotation.DefaultWeavingExtensions; 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.weaving.CollectionPojoWeaver; -import de.siphalor.tweed5.weaver.pojo.api.weaving.CompoundPojoWeaver; -import de.siphalor.tweed5.weaver.pojo.api.weaving.TrivialPojoWeaver; import lombok.Data; import org.junit.jupiter.api.Test; @@ -65,6 +65,7 @@ class TweedPojoWeaverBootstrapperTest { } @PojoWeaving(extensions = {DummyExtension.class}) + @DefaultWeavingExtensions @CompoundWeaving(namingFormat = "camel_case") @Data public static class MainCompound { @@ -88,7 +89,9 @@ class TweedPojoWeaverBootstrapperTest { boolean somethingElse; } - @PojoWeaving(weavers = {CompoundPojoWeaver.class, CollectionPojoWeaver.class, TrivialPojoWeaver.class}) + @PojoWeaving + @PojoWeavingExtension(CollectionPojoWeaver.class) + @DefaultWeavingExtensions @CompoundWeaving(namingFormat = "camel_case") @Data public static class CompoundWithList {