From a50ce563e64517216415ad791beb4c399fb77700 Mon Sep 17 00:00:00 2001 From: Siphalor Date: Sun, 20 Apr 2025 21:04:21 +0200 Subject: [PATCH] [weaver-pojo] Coherent collection weaving --- .../annotation/CoherentCollectionWeaving.java | 14 ++ ...WeavableCoherentCollectionConfigEntry.java | 40 ++++++ .../weaving/CoherentCollectionPojoWeaver.java | 125 ++++++++++++++++++ .../pojo/api/weaving/CompoundPojoWeaver.java | 2 + .../pojo/api/weaving/TrivialPojoWeaver.java | 4 +- .../pojo/api/weaving/WeavingContext.java | 2 +- .../CoherentCollectionConfigEntryImpl.java | 68 ++++++++++ .../CoherentCollectionWeavingConfig.java | 10 ++ .../CoherentCollectionWeavingConfigImpl.java | 21 +++ .../compound/CompoundWeavingConfigImpl.java | 1 - .../TweedPojoWeaverBootstrapperTest.java | 60 ++++++--- .../pojo/test/ConfigEntryAssertions.java | 19 +++ 12 files changed, 347 insertions(+), 19 deletions(-) create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/CoherentCollectionWeaving.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCoherentCollectionConfigEntry.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CoherentCollectionPojoWeaver.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CoherentCollectionConfigEntryImpl.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfig.java create mode 100644 tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfigImpl.java diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/CoherentCollectionWeaving.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/CoherentCollectionWeaving.java new file mode 100644 index 0000000..7685143 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/annotation/CoherentCollectionWeaving.java @@ -0,0 +1,14 @@ +package de.siphalor.tweed5.weaver.pojo.api.annotation; + +import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCoherentCollectionConfigEntry; + +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.TYPE_USE}) +public @interface CoherentCollectionWeaving { + Class entryClass() default WeavableCoherentCollectionConfigEntry.class; +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCoherentCollectionConfigEntry.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCoherentCollectionConfigEntry.java new file mode 100644 index 0000000..c6831b6 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/entry/WeavableCoherentCollectionConfigEntry.java @@ -0,0 +1,40 @@ +package de.siphalor.tweed5.weaver.pojo.api.entry; + +import de.siphalor.tweed5.core.api.entry.CoherentCollectionConfigEntry; +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoWeavingException; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.function.IntFunction; + +/** + * {@inheritDoc} + *
+ * A constructor taking the value {@link Class} + * and a {@link java.util.function.IntFunction} that allows to instantiate the value class with a single capacity argument. + */ +public interface WeavableCoherentCollectionConfigEntry> + extends CoherentCollectionConfigEntry { + static , C extends WeavableCoherentCollectionConfigEntry> C instantiate( + Class weavableClass, Class valueClass, IntFunction constructor + ) throws PojoWeavingException { + try { + Constructor weavableEntryConstructor = weavableClass.getConstructor(Class.class, IntFunction.class); + return weavableEntryConstructor.newInstance(valueClass, constructor); + } catch (NoSuchMethodException e) { + throw new PojoWeavingException( + "Class " + weavableClass.getName() + " must have constructor with value class and value constructor", + e + ); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new PojoWeavingException( + "Failed to instantiate class for weavable collection entry " + weavableClass.getName(), + e + ); + } + } + + void elementEntry(ConfigEntry elementEntry); +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CoherentCollectionPojoWeaver.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CoherentCollectionPojoWeaver.java new file mode 100644 index 0000000..e3757e9 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CoherentCollectionPojoWeaver.java @@ -0,0 +1,125 @@ +package de.siphalor.tweed5.weaver.pojo.api.weaving; + +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData; +import de.siphalor.tweed5.typeutils.api.type.ActualType; +import de.siphalor.tweed5.weaver.pojo.api.annotation.CoherentCollectionWeaving; +import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCoherentCollectionConfigEntry; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.PojoWeavingException; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.coherentcollection.CoherentCollectionWeavingConfig; +import de.siphalor.tweed5.weaver.pojo.impl.weaving.coherentcollection.CoherentCollectionWeavingConfigImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.AnnotatedElement; +import java.util.*; +import java.util.function.IntFunction; + +public class CoherentCollectionPojoWeaver implements TweedPojoWeaver { + private static final CoherentCollectionWeavingConfig DEFAULT_WEAVING_CONFIG = CoherentCollectionWeavingConfigImpl.builder() + .coherentCollectionEntryClass(de.siphalor.tweed5.weaver.pojo.impl.entry.CoherentCollectionConfigEntryImpl.class) + .build(); + + private RegisteredExtensionData weavingConfigAccess; + + @Override + public void setup(SetupContext context) { + this.weavingConfigAccess = context.registerWeavingContextExtensionData(CoherentCollectionWeavingConfig.class); + } + + @Override + public @Nullable ConfigEntry weaveEntry(ActualType valueType, WeavingContext context) { + List> collectionTypeParams = valueType.getTypesOfSuperArguments(Collection.class); + if (collectionTypeParams == null) { + return null; + } + try { + CoherentCollectionWeavingConfig weavingConfig = getOrCreateWeavingConfig(context); + WeavingContext.ExtensionsData newExtensionsData = context.extensionsData().copy(); + weavingConfigAccess.set(newExtensionsData, weavingConfig); + + IntFunction> constructor = getCollectionConstructor(valueType); + + //noinspection unchecked,rawtypes + WeavableCoherentCollectionConfigEntry configEntry = WeavableCoherentCollectionConfigEntry.instantiate( + (Class) weavingConfig.coherentCollectionEntryClass(), + (Class) valueType.declaredType(), + constructor + ); + + configEntry.elementEntry(context.weaveEntry( + collectionTypeParams.get(0), + context.subContextBuilder("element") + .annotations(collectionTypeParams.get(0)) + .extensionsData(newExtensionsData) + .build() + )); + configEntry.seal(context.configContainer()); + + return configEntry; + } catch (Exception e) { + throw new PojoWeavingException("Exception occurred trying to weave collectoin for class " + valueType, e); + } + } + + private CoherentCollectionWeavingConfig getOrCreateWeavingConfig(WeavingContext context) { + CoherentCollectionWeavingConfig parent; + if (context.extensionsData().isPatchworkPartSet(CoherentCollectionWeavingConfig.class)) { + parent = (CoherentCollectionWeavingConfig) context.extensionsData(); + } else { + parent = DEFAULT_WEAVING_CONFIG; + } + + CoherentCollectionWeavingConfig local = createWeavingConfigFromAnnotations(context.annotations()); + if (local == null) { + return parent; + } + + return CoherentCollectionWeavingConfigImpl.withOverrides(parent, local); + } + + private CoherentCollectionWeavingConfig createWeavingConfigFromAnnotations(@NotNull AnnotatedElement annotations) { + CoherentCollectionWeaving annotation = annotations.getAnnotation(CoherentCollectionWeaving.class); + if (annotation == null) { + return null; + } + + CoherentCollectionWeavingConfigImpl.CoherentCollectionWeavingConfigImplBuilder builder = CoherentCollectionWeavingConfigImpl.builder(); + if (annotation.entryClass() != null) { + builder.coherentCollectionEntryClass(annotation.entryClass()); + } + + return builder.build(); + } + + public IntFunction> getCollectionConstructor(ActualType type) { + if (type.declaredType() == List.class) { + return ArrayList::new; + } else if (type.declaredType() == Set.class) { + return capacity -> new HashSet<>((int) Math.ceil(capacity * 1.4), 0.75F); + } + try { + return findCompatibleConstructor(type); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new PojoWeavingException("could not find no args constructor for " + type, e); + } + } + + public IntFunction> findCompatibleConstructor(ActualType type) throws + NoSuchMethodException, + IllegalAccessException { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + MethodHandle constructor = lookup.findConstructor(type.declaredType(), MethodType.methodType(Void.class)); + return capacity -> { + try { + //noinspection unchecked + return (Collection) constructor.invoke(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + }; + } +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaver.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/CompoundPojoWeaver.java index 939ef4f..acc4493 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 @@ -63,6 +63,8 @@ public class CompoundPojoWeaver implements TweedPojoWeaver { } }); + compoundEntry.seal(context.configContainer()); + return compoundEntry; } catch (Exception e) { throw new PojoWeavingException("Exception occurred trying to weave compound for class " + valueType, e); 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 1932650..01000ca 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 @@ -13,6 +13,8 @@ public class TrivialPojoWeaver implements TweedPojoWeaver { @Override public @Nullable ConfigEntry weaveEntry(ActualType valueType, WeavingContext context) { - return new SimpleConfigEntryImpl<>(valueType.declaredType()); + SimpleConfigEntryImpl entry = new SimpleConfigEntryImpl<>(valueType.declaredType()); + entry.seal(context.configContainer()); + return entry; } } 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 621b5ea..ce59fbf 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 @@ -36,7 +36,7 @@ public class WeavingContext implements TweedPojoWeavingFunction.NonNull { return new Builder(null, weavingFunction, configContainer, new String[]{ baseName }); } - public Builder subContextBuilder(String subPathName) { + public Builder subContextBuilder(@NotNull String subPathName) { String[] newPath = Arrays.copyOf(path, path.length + 1); newPath[path.length] = subPathName; return new Builder(this, weavingFunction, configContainer, newPath).extensionsData(extensionsData); diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CoherentCollectionConfigEntryImpl.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CoherentCollectionConfigEntryImpl.java new file mode 100644 index 0000000..f953fef --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/CoherentCollectionConfigEntryImpl.java @@ -0,0 +1,68 @@ +package de.siphalor.tweed5.weaver.pojo.impl.entry; + +import de.siphalor.tweed5.core.api.entry.BaseConfigEntry; +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor; +import de.siphalor.tweed5.core.api.entry.ConfigEntryVisitor; +import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCoherentCollectionConfigEntry; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.function.IntFunction; + +@Getter +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class CoherentCollectionConfigEntryImpl> extends BaseConfigEntry implements WeavableCoherentCollectionConfigEntry { + private final IntFunction constructor; + private ConfigEntry elementEntry; + + public CoherentCollectionConfigEntryImpl(@NotNull Class valueClass, IntFunction constructor) { + super(valueClass); + this.constructor = constructor; + } + + @Override + public void elementEntry(ConfigEntry elementEntry) { + this.elementEntry = elementEntry; + } + + @Override + public T instantiateCollection(int size) { + try { + return constructor.apply(size); + } catch (Throwable e) { + throw new IllegalStateException("Failed to instantiate collection class", e); + } + } + + @Override + public void visitInOrder(ConfigEntryVisitor visitor) { + if (visitor.enterCollectionEntry(this)) { + elementEntry.visitInOrder(visitor); + visitor.leaveCollectionEntry(this); + } + } + + @Override + public void visitInOrder(ConfigEntryValueVisitor visitor, T value) { + if (visitor.enterCollectionEntry(this, value)) { + for (E element : value) { + elementEntry.visitInOrder(visitor, element); + } + visitor.leaveCollectionEntry(this, value); + } + } + + @Override + public @NotNull T deepCopy(@NotNull T value) { + T copy = instantiateCollection(value.size()); + for (E element : value) { + copy.add(elementEntry.deepCopy(element)); + } + return copy; + } +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfig.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfig.java new file mode 100644 index 0000000..c421ab8 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfig.java @@ -0,0 +1,10 @@ +package de.siphalor.tweed5.weaver.pojo.impl.weaving.coherentcollection; + +import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCoherentCollectionConfigEntry; +import org.jetbrains.annotations.Nullable; + +public interface CoherentCollectionWeavingConfig { + @SuppressWarnings("rawtypes") + @Nullable + Class coherentCollectionEntryClass(); +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfigImpl.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfigImpl.java new file mode 100644 index 0000000..9ac2c40 --- /dev/null +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/coherentcollection/CoherentCollectionWeavingConfigImpl.java @@ -0,0 +1,21 @@ +package de.siphalor.tweed5.weaver.pojo.impl.weaving.coherentcollection; + +import de.siphalor.tweed5.weaver.pojo.api.entry.WeavableCoherentCollectionConfigEntry; +import lombok.Builder; +import lombok.Value; +import org.jetbrains.annotations.Nullable; + +@Builder +@Value +public class CoherentCollectionWeavingConfigImpl implements CoherentCollectionWeavingConfig { + + @SuppressWarnings("rawtypes") + @Nullable + Class coherentCollectionEntryClass; + + public static CoherentCollectionWeavingConfigImpl withOverrides(CoherentCollectionWeavingConfig self, CoherentCollectionWeavingConfig overrides) { + return CoherentCollectionWeavingConfigImpl.builder() + .coherentCollectionEntryClass(overrides.coherentCollectionEntryClass() != null ? overrides.coherentCollectionEntryClass() : self.coherentCollectionEntryClass()) + .build(); + } +} diff --git a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/compound/CompoundWeavingConfigImpl.java b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/compound/CompoundWeavingConfigImpl.java index b8d03fc..f78aea9 100644 --- a/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/compound/CompoundWeavingConfigImpl.java +++ b/tweed5-weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/weaving/compound/CompoundWeavingConfigImpl.java @@ -9,7 +9,6 @@ import org.jetbrains.annotations.Nullable; @Builder @Value public class CompoundWeavingConfigImpl implements CompoundWeavingConfig { - private static final CompoundWeavingConfigImpl EMPTY = CompoundWeavingConfigImpl.builder().build(); NamingFormat compoundSourceNamingFormat; NamingFormat compoundTargetNamingFormat; 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 5143063..abcf363 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 @@ -5,30 +5,35 @@ 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.PojoWeaving; +import de.siphalor.tweed5.weaver.pojo.api.weaving.CoherentCollectionPojoWeaver; +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; -import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isCompoundEntryForClassWith; -import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isSimpleEntryForClass; +import java.util.List; + +import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.*; import static org.assertj.core.api.Assertions.assertThat; @SuppressWarnings("unused") class TweedPojoWeaverBootstrapperTest { @Test void defaultWeaving() { - TweedPojoWeaverBootstrapper bootstrapper = TweedPojoWeaverBootstrapper.create(DefaultWeaving.class); - ConfigContainer configContainer = bootstrapper.weave(); + TweedPojoWeaverBootstrapper bootstrapper = TweedPojoWeaverBootstrapper.create(MainCompound.class); + ConfigContainer configContainer = bootstrapper.weave(); - assertThat(configContainer.rootEntry()).satisfies(isCompoundEntryForClassWith(DefaultWeaving.class, rootCompound -> - assertThat(rootCompound.subEntries()) - .hasEntrySatisfying("primitiveInteger", isSimpleEntryForClass(int.class)) - .hasEntrySatisfying("boxedDouble", isSimpleEntryForClass(Double.class)) - .hasEntrySatisfying("value", isSimpleEntryForClass(InnerValue.class)) - .hasEntrySatisfying("compound", isCompoundEntryForClassWith(InnerCompound.class, innerCompound -> - assertThat(innerCompound.subEntries()) - .hasEntrySatisfying("string", isSimpleEntryForClass(String.class)) - .hasSize(1))) - .hasSize(4) + assertThat(configContainer.rootEntry()).satisfies(isCompoundEntryForClassWith(MainCompound.class, rootCompound -> + assertThat(rootCompound.subEntries()) + .hasEntrySatisfying("primitiveInteger", isSimpleEntryForClass(int.class)) + .hasEntrySatisfying("boxedDouble", isSimpleEntryForClass(Double.class)) + .hasEntrySatisfying("value", isSimpleEntryForClass(InnerValue.class)) + .hasEntrySatisfying("list", isSimpleEntryForClass(List.class)) + .hasEntrySatisfying("compound", isCompoundEntryForClassWith(InnerCompound.class, innerCompound -> + assertThat(innerCompound.subEntries()) + .hasEntrySatisfying("string", isSimpleEntryForClass(String.class)) + .hasSize(1))) + .hasSize(5) )); configContainer.initialize(); @@ -38,6 +43,21 @@ class TweedPojoWeaverBootstrapperTest { .hasSize(1); } + @Test + void weavingWithList() { + TweedPojoWeaverBootstrapper bootstrapper = TweedPojoWeaverBootstrapper.create(CompoundWithList.class); + ConfigContainer configContainer = bootstrapper.weave(); + + assertThat(configContainer.rootEntry()).satisfies(isCompoundEntryForClassWith(CompoundWithList.class, rootCompound -> + assertThat(rootCompound.subEntries()) + .hasEntrySatisfying("strings", isCollectionEntryForClass( + List.class, + list -> assertThat(list.elementEntry()).satisfies(isSimpleEntryForClass(String.class)) + + )) + )); + } + @AutoService(DummyExtension.class) public static class DummyExtension implements TweedExtension { @Override @@ -49,10 +69,11 @@ class TweedPojoWeaverBootstrapperTest { @PojoWeaving(extensions = {DummyExtension.class}) @CompoundWeaving(namingFormat = "camel_case") @Data - public static class DefaultWeaving { + public static class MainCompound { int primitiveInteger; Double boxedDouble; InnerValue value; + List list; InnerCompound compound; } @@ -68,4 +89,11 @@ class TweedPojoWeaverBootstrapperTest { int something; boolean somethingElse; } -} \ No newline at end of file + + @PojoWeaving(weavers = {CompoundPojoWeaver.class, CoherentCollectionPojoWeaver.class, TrivialPojoWeaver.class}) + @CompoundWeaving(namingFormat = "camel_case") + @Data + public static class CompoundWithList { + List strings; + } +} diff --git a/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/test/ConfigEntryAssertions.java b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/test/ConfigEntryAssertions.java index 21dc891..136bc91 100644 --- a/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/test/ConfigEntryAssertions.java +++ b/tweed5-weaver-pojo/src/test/java/de/siphalor/tweed5/weaver/pojo/test/ConfigEntryAssertions.java @@ -1,9 +1,12 @@ package de.siphalor.tweed5.weaver.pojo.test; +import de.siphalor.tweed5.core.api.entry.CoherentCollectionConfigEntry; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry; +import de.siphalor.tweed5.weaver.pojo.api.weaving.CoherentCollectionPojoWeaver; +import java.util.Collection; import java.util.function.Consumer; import static org.assertj.core.api.Assertions.assertThat; @@ -33,4 +36,20 @@ public class ConfigEntryAssertions { condition::accept ); } + + public static Consumer isCollectionEntryForClass( + Class collectionClass, + Consumer> condition + ) { + return object -> assertThat(object) + .as("Should be a collection config entry for class " + collectionClass.getName()) + .asInstanceOf(type(CoherentCollectionConfigEntry.class)) + .as("Collection entry for class " + collectionClass.getSimpleName()) + .satisfies( + listEntry -> assertThat(listEntry.valueClass()) + .as("Value class of collection entry should match") + .isEqualTo(collectionClass), + condition::accept + ); + } }