diff --git a/tweed5/core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java b/tweed5/core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java index 2c4031a..5d611e3 100644 --- a/tweed5/core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java +++ b/tweed5/core/src/main/java/de/siphalor/tweed5/core/api/entry/ConfigEntry.java @@ -2,7 +2,6 @@ package de.siphalor.tweed5.core.api.entry; import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.patchwork.api.Patchwork; -import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import java.util.function.Consumer; @@ -26,7 +25,7 @@ public interface ConfigEntry { } void visitInOrder(ConfigEntryVisitor visitor); - void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value); + void visitInOrder(ConfigEntryValueVisitor visitor, T value); - T deepCopy(@NonNull T value); + T deepCopy(T value); } diff --git a/tweed5/core/src/main/java/de/siphalor/tweed5/core/api/entry/NullableConfigEntry.java b/tweed5/core/src/main/java/de/siphalor/tweed5/core/api/entry/NullableConfigEntry.java new file mode 100644 index 0000000..dbe88c6 --- /dev/null +++ b/tweed5/core/src/main/java/de/siphalor/tweed5/core/api/entry/NullableConfigEntry.java @@ -0,0 +1,17 @@ +package de.siphalor.tweed5.core.api.entry; + +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; + +public interface NullableConfigEntry extends StructuredConfigEntry { + String NON_NULL_KEY = ":nonNull"; + + @Override + default NullableConfigEntry apply(Consumer> function) { + StructuredConfigEntry.super.apply(function); + return this; + } + + ConfigEntry nonNullEntry(); +} diff --git a/tweed5/core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java b/tweed5/core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java index 7bc9642..78ce291 100644 --- a/tweed5/core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java +++ b/tweed5/core/src/main/java/de/siphalor/tweed5/core/impl/DefaultConfigContainer.java @@ -16,7 +16,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; -public class DefaultConfigContainer implements ConfigContainer { +public class DefaultConfigContainer implements ConfigContainer { @Getter private ConfigContainerSetupPhase setupPhase = ConfigContainerSetupPhase.EXTENSIONS_SETUP; private final Set> requestedExtensions = new HashSet<>(); diff --git a/tweed5/core/src/main/java/de/siphalor/tweed5/core/impl/entry/NullableConfigEntryImpl.java b/tweed5/core/src/main/java/de/siphalor/tweed5/core/impl/entry/NullableConfigEntryImpl.java new file mode 100644 index 0000000..75076e0 --- /dev/null +++ b/tweed5/core/src/main/java/de/siphalor/tweed5/core/impl/entry/NullableConfigEntryImpl.java @@ -0,0 +1,52 @@ +package de.siphalor.tweed5.core.impl.entry; + +import de.siphalor.tweed5.core.api.container.ConfigContainer; +import de.siphalor.tweed5.core.api.entry.BaseConfigEntry; +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.entry.ConfigEntryValueVisitor; +import de.siphalor.tweed5.core.api.entry.NullableConfigEntry; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Map; + +@Getter +public class NullableConfigEntryImpl extends BaseConfigEntry implements NullableConfigEntry { + private final ConfigEntry nonNullEntry; + + public NullableConfigEntryImpl( + ConfigContainer container, + Class valueClass, + ConfigEntry nonNullEntry + ) { + super(container, valueClass); + this.nonNullEntry = nonNullEntry; + } + + @Override + public Map> subEntries() { + return Collections.singletonMap(NON_NULL_KEY, nonNullEntry); + } + + @Override + public void visitInOrder(ConfigEntryValueVisitor visitor, T value) { + if (value != null) { + if (visitor.enterStructuredEntry(this, value)) { + if (visitor.enterStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY)) { + nonNullEntry.visitInOrder(visitor, value); + visitor.leaveStructuredSubEntry(NON_NULL_KEY, NON_NULL_KEY); + } + visitor.leaveStructuredEntry(this, value); + } + } + } + + @Override + public T deepCopy(T value) { + if (value != null) { + return nonNullEntry.deepCopy(value); + } + return null; + } +} diff --git a/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java index 1a470a7..3d2dd45 100644 --- a/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java +++ b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/comment/impl/CommentExtensionImplTest.java @@ -1,10 +1,12 @@ package de.siphalor.tweed5.defaultextensions.comment.impl; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; +import de.siphalor.tweed5.core.api.entry.NullableConfigEntry; import de.siphalor.tweed5.core.api.entry.SimpleConfigEntry; import de.siphalor.tweed5.core.api.extension.TweedExtension; import de.siphalor.tweed5.core.api.middleware.Middleware; import de.siphalor.tweed5.core.impl.DefaultConfigContainer; +import de.siphalor.tweed5.core.impl.entry.NullableConfigEntryImpl; import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl; import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl; import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; @@ -36,7 +38,7 @@ class CommentExtensionImplTest { private DefaultConfigContainer<@NonNull Map> configContainer; private CompoundConfigEntry> rootEntry; - private SimpleConfigEntry intEntry; + private NullableConfigEntry intEntry; private SimpleConfigEntry stringEntry; private SimpleConfigEntry noCommentEntry; @@ -49,9 +51,13 @@ class CommentExtensionImplTest { configContainer.registerExtensions(extraExtensions); configContainer.finishExtensionSetup(); - intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) - .apply(entryReaderWriter(intReaderWriter())) - .apply(baseComment("It is an integer")); + intEntry = new NullableConfigEntryImpl<>( + configContainer, Integer.class, + new SimpleConfigEntryImpl<>(configContainer, Integer.class) + .apply(entryReaderWriter(intReaderWriter())) + .apply(baseComment("It is an integer"))) + .apply(entryReaderWriter(nullableReaderWriter())) + .apply(baseComment("This is nullable")); stringEntry = new SimpleConfigEntryImpl<>(configContainer, String.class) .apply(entryReaderWriter(stringReaderWriter())) .apply(baseComment("It is a string")); @@ -80,7 +86,7 @@ class CommentExtensionImplTest { configContainer.initialize(); CommentExtension commentExtension = configContainer.extension(CommentExtension.class).orElseThrow(); - assertEquals("It is an integer", commentExtension.getFullComment(intEntry)); + assertEquals("This is nullable", commentExtension.getFullComment(intEntry)); assertEquals("It is a string", commentExtension.getFullComment(stringEntry)); assertNull(commentExtension.getFullComment(noCommentEntry)); } @@ -91,7 +97,7 @@ class CommentExtensionImplTest { configContainer.initialize(); CommentExtension commentExtension = configContainer.extension(CommentExtension.class).orElseThrow(); - assertEquals("The comment is:\nIt is an integer\nEND", commentExtension.getFullComment(intEntry)); + assertEquals("The comment is:\nThis is nullable\nEND", commentExtension.getFullComment(intEntry)); assertEquals("The comment is:\nIt is a string\nEND", commentExtension.getFullComment(stringEntry)); assertEquals("The comment is:\n\nEND", commentExtension.getFullComment(noCommentEntry)); } @@ -120,6 +126,7 @@ class CommentExtensionImplTest { // This is the root value. // It is the topmost value in the tree. { + \t// This is nullable \t// It is an integer \tint: 123 \t// It is a string diff --git a/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/patch/impl/PatchExtensionImplTest.java b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/patch/impl/PatchExtensionImplTest.java index f75f4cb..e262065 100644 --- a/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/patch/impl/PatchExtensionImplTest.java +++ b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/patch/impl/PatchExtensionImplTest.java @@ -5,6 +5,7 @@ import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry; import de.siphalor.tweed5.core.impl.DefaultConfigContainer; import de.siphalor.tweed5.core.impl.entry.CollectionConfigEntryImpl; +import de.siphalor.tweed5.core.impl.entry.NullableConfigEntryImpl; import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl; import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl; import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; @@ -51,40 +52,39 @@ class PatchExtensionImplTest { var int1Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) .apply(entryReaderWriter(intReaderWriter())); - var int2Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) - .apply(entryReaderWriter( - nullableReader(intReaderWriter()), - nullableWriter(intReaderWriter()) - )); - var listEntry = new CollectionConfigEntryImpl<>( + var int2Entry = new NullableConfigEntryImpl<>( + configContainer, Integer.class, + new SimpleConfigEntryImpl<>(configContainer, Integer.class) + .apply(entryReaderWriter(intReaderWriter()))); + var listEntry = new NullableConfigEntryImpl<>( configContainer, (Class>)(Class) List.class, - ArrayList::new, - new SimpleConfigEntryImpl<>(configContainer, Integer.class) - .apply(entryReaderWriter(intReaderWriter())) - ) - .apply(entryReaderWriter( - nullableReader(collectionReaderWriter()), - nullableWriter(collectionReaderWriter()) - )); + new CollectionConfigEntryImpl<>( + configContainer, + (Class>)(Class) List.class, + ArrayList::new, + new SimpleConfigEntryImpl<>(configContainer, Integer.class) + .apply(entryReaderWriter(intReaderWriter())) + ).apply(entryReaderWriter(collectionReaderWriter())) + ).apply(entryReaderWriter(nullableReaderWriter())); var nestedInt1Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) .apply(entryReaderWriter(intReaderWriter())); var nestedInt2Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) .apply(entryReaderWriter(intReaderWriter())); - var compoundEntry = new StaticMapCompoundConfigEntryImpl<>( + var compoundEntry = new NullableConfigEntryImpl<>( configContainer, (Class>)(Class) Map.class, - HashMap::new, - sequencedMap(List.of( - entry("int1", nestedInt1Entry), - entry("int2", nestedInt2Entry) - )) - ) - .apply(entryReaderWriter( - nullableReader(compoundReaderWriter()), - nullableWriter(compoundReaderWriter()) - )); + new StaticMapCompoundConfigEntryImpl<>( + configContainer, + (Class>)(Class) Map.class, + HashMap::new, + sequencedMap(List.of( + entry("int1", nestedInt1Entry), + entry("int2", nestedInt2Entry) + )) + ).apply(entryReaderWriter(compoundReaderWriter())) + ).apply(entryReaderWriter(nullableReaderWriter())); rootEntry = new StaticMapCompoundConfigEntryImpl<>( configContainer, diff --git a/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReaderTest.java b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReaderTest.java index 0566cbf..ae41c5d 100644 --- a/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReaderTest.java +++ b/tweed5/default-extensions/src/test/java/de/siphalor/tweed5/defaultextensions/pather/api/PathTrackingDataReaderTest.java @@ -1,6 +1,5 @@ package de.siphalor.tweed5.defaultextensions.pather.api; -import de.siphalor.tweed5.dataapi.api.TweedDataReadException; import de.siphalor.tweed5.dataapi.api.TweedDataReader; import de.siphalor.tweed5.dataapi.api.TweedDataToken; import de.siphalor.tweed5.dataapi.api.TweedDataTokens; diff --git a/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/readwrite/TweedEntryReaderWriters.java b/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/readwrite/TweedEntryReaderWriters.java index 404bbf4..cf8d74c 100644 --- a/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/readwrite/TweedEntryReaderWriters.java +++ b/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/api/readwrite/TweedEntryReaderWriters.java @@ -3,12 +3,14 @@ package de.siphalor.tweed5.data.extension.api.readwrite; import de.siphalor.tweed5.core.api.entry.CollectionConfigEntry; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.entry.NullableConfigEntry; import de.siphalor.tweed5.data.extension.api.TweedEntryReader; import de.siphalor.tweed5.data.extension.api.TweedEntryWriter; import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import java.util.Collection; @@ -51,12 +53,25 @@ public class TweedEntryReaderWriters { return (TweedEntryReaderWriter>)(TweedEntryReaderWriter) TweedEntryReaderWriterImpls.ENUM_READER_WRITER; } - public static > TweedEntryReader nullableReader(TweedEntryReader delegate) { - return new TweedEntryReaderWriterImpls.NullableReader<>(delegate); + public static TweedEntryReaderWriter> nullableReaderWriter() { + //noinspection unchecked + return (TweedEntryReaderWriter>) (TweedEntryReaderWriter) TweedEntryReaderWriterImpls.NULLABLE_READER_WRITER; } + /** + * @deprecated You probably want to use {@link #nullableReaderWriter()} instead. + */ + @Deprecated + public static > TweedEntryReader nullableReader(TweedEntryReader delegate) { + return new TweedEntryReaderWriterImpls.FixedNullableReader<>(delegate); + } + + /** + * @deprecated You probably want to use {@link #nullableReaderWriter()} instead. + */ + @Deprecated public static > TweedEntryWriter nullableWriter(TweedEntryWriter delegate) { - return new TweedEntryReaderWriterImpls.NullableWriter<>(delegate); + return new TweedEntryReaderWriterImpls.FixedNullableWriter<>(delegate); } public static > TweedEntryReaderWriter> collectionReaderWriter() { diff --git a/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/DefaultTweedEntryReaderWriterImplsProvider.java b/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/DefaultTweedEntryReaderWriterImplsProvider.java index fc2a2fb..04b2153 100644 --- a/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/DefaultTweedEntryReaderWriterImplsProvider.java +++ b/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/DefaultTweedEntryReaderWriterImplsProvider.java @@ -4,6 +4,10 @@ import com.google.auto.service.AutoService; 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.api.readwrite.TweedEntryReaderWriters; +import lombok.RequiredArgsConstructor; + +import java.util.function.Function; import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*; @@ -39,22 +43,32 @@ public class DefaultTweedEntryReaderWriterImplsProvider implements TweedReaderWr context.registerWriterFactory("tweed5.string", new StaticReaderWriterFactory<>(stringReaderWriter())); context.registerReaderFactory("tweed5.enum", new StaticReaderWriterFactory<>(enumReaderWriter())); context.registerWriterFactory("tweed5.enum", new StaticReaderWriterFactory<>(enumReaderWriter())); + context.registerReaderFactory("tweed5.nullable", new NullableReaderWriterFactory<>( + TweedEntryReaderWriters::nullableReader + )); + context.registerWriterFactory("tweed5.nullable", new NullableReaderWriterFactory<>( + TweedEntryReaderWriters::nullableWriter + )); context.registerReaderFactory("tweed5.collection", new StaticReaderWriterFactory<>(collectionReaderWriter())); context.registerWriterFactory("tweed5.collection", new StaticReaderWriterFactory<>(collectionReaderWriter())); context.registerReaderFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter())); context.registerWriterFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter())); + } - context.registerReaderFactory("tweed5.nullable", delegateReaders -> { - if (delegateReaders.length != 1) { - throw new IllegalArgumentException("Nullable reader requires a single delegate argument, got " + delegateReaders.length); + @RequiredArgsConstructor + private static class NullableReaderWriterFactory implements ReaderWriterFactory { + private final Function delegateBasedFactory; + + @Override + public T create(T... delegateReaderWriters) { + if (delegateReaderWriters.length == 0) { + //noinspection unchecked + return (T) TweedEntryReaderWriters.nullableReaderWriter(); + } else if (delegateReaderWriters.length == 1) { + return delegateBasedFactory.apply(delegateReaderWriters[0]); + } else { + throw new IllegalArgumentException("Nullable readers and writers may have only one or zero delegates"); } - return nullableReader(delegateReaders[0]); - }); - context.registerWriterFactory("tweed5.nullable", delegateWriters -> { - if (delegateWriters.length != 1) { - throw new IllegalArgumentException("Nullable writer requires a single delegate argument, got " + delegateWriters.length); - } - return nullableWriter(delegateWriters[0]); - }); + } } } diff --git a/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java b/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java index 1477dc2..cc3bade 100644 --- a/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java +++ b/tweed5/serde-extension/src/main/java/de/siphalor/tweed5/data/extension/impl/TweedEntryReaderWriterImpls.java @@ -3,6 +3,7 @@ package de.siphalor.tweed5.data.extension.impl; import de.siphalor.tweed5.core.api.entry.CollectionConfigEntry; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.entry.NullableConfigEntry; import de.siphalor.tweed5.data.extension.api.*; import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter; import de.siphalor.tweed5.dataapi.api.*; @@ -28,13 +29,48 @@ public class TweedEntryReaderWriterImpls { public static final TweedEntryReaderWriter> STRING_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsString, TweedDataVisitor::visitString); public static final TweedEntryReaderWriter, ConfigEntry>> ENUM_READER_WRITER = new EnumReaderWriter<>(); + public static final TweedEntryReaderWriter> NULLABLE_READER_WRITER = new NullableReaderWriter<>(); public static final TweedEntryReaderWriter, CollectionConfigEntry>> COLLECTION_READER_WRITER = new CollectionReaderWriter<>(); public static final TweedEntryReaderWriter> COMPOUND_READER_WRITER = new CompoundReaderWriter<>(); public static final TweedEntryReaderWriter> NOOP_READER_WRITER = new NoopReaderWriter(); + public static class NullableReaderWriter implements TweedEntryReaderWriter> { + @Override + public T read(TweedDataReader reader, NullableConfigEntry entry, TweedReadContext context) + throws TweedEntryReadException { + try { + if (reader.peekToken().isNull()) { + reader.readToken(); + return null; + } + } catch (TweedDataReadException e) { + throw new TweedEntryReadException(e, context); + } + + TweedEntryReader> nonNullReader = context.readWriteExtension().getReaderChain(entry.nonNullEntry()); + return nonNullReader.read(reader, entry.nonNullEntry(), context); + } + + @Override + public void write( + TweedDataVisitor writer, + @Nullable T value, + NullableConfigEntry entry, + TweedWriteContext context + ) throws TweedEntryWriteException, TweedDataWriteException { + if (value == null) { + writer.visitNull(); + } else { + TweedEntryWriter> nonNullWriter = context.readWriteExtension().getWriterChain(entry.nonNullEntry()); + nonNullWriter.write(writer, value, entry.nonNullEntry(), context); + } + } + } + + @Deprecated @RequiredArgsConstructor - public static class NullableReader> implements TweedEntryReader { + public static class FixedNullableReader> implements TweedEntryReader { private final TweedEntryReader delegate; @Override @@ -51,8 +87,9 @@ public class TweedEntryReaderWriterImpls { } } + @Deprecated @RequiredArgsConstructor - public static class NullableWriter> implements TweedEntryWriter { + public static class FixedNullableWriter> implements TweedEntryWriter { private final TweedEntryWriter delegate; @Override diff --git a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessor.java b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessor.java index 432c254..330c89e 100644 --- a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessor.java +++ b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/auto/AutoReadWritePojoWeavingProcessor.java @@ -2,10 +2,12 @@ package de.siphalor.tweed5.weaver.pojoext.serde.api.auto; import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.entry.NullableConfigEntry; 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.api.readwrite.TweedEntryReaderWriters; import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; import de.siphalor.tweed5.patchwork.api.Patchwork; import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; @@ -113,6 +115,15 @@ public class AutoReadWritePojoWeavingProcessor implements TweedPojoWeavingExtens @Override public void afterWeaveEntry(ActualType valueType, ConfigEntry configEntry, WeavingContext context) { + if (configEntry instanceof NullableConfigEntry) { + readWriteExtension.setEntryReaderWriter( + configEntry, + TweedEntryReaderWriters.nullableReaderWriter(), + TweedEntryReaderWriters.nullableReaderWriter() + ); + return; + } + assert customDataAccess != null; CustomData customData = context.extensionsData().get(customDataAccess); if (customData == null || customData.mappings().isEmpty()) { diff --git a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWriteBehavior.java b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWriteBehavior.java index f4b696e..2a2f936 100644 --- a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWriteBehavior.java +++ b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWriteBehavior.java @@ -5,6 +5,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * @see AutoNullableReadWritePojoWeavingProcessor + */ +@Deprecated @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) public @interface AutoNullableReadWriteBehavior { diff --git a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessor.java b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessor.java index 1676485..deedf2a 100644 --- a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessor.java +++ b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoNullableReadWritePojoWeavingProcessor.java @@ -17,6 +17,11 @@ import org.jspecify.annotations.Nullable; import java.lang.annotation.Annotation; +/** + * @deprecated The recommended approach is to use {@link de.siphalor.tweed5.weaver.pojo.api.weaving.NullablePojoWeaver} + * and set @{@link Nullable} on all nullable entries. + */ +@Deprecated public class AutoNullableReadWritePojoWeavingProcessor implements TweedPojoWeavingExtension { private final ReadWriteExtension readWriteExtension; private @Nullable PatchworkPartAccess customDataAccess; @@ -71,14 +76,14 @@ public class AutoNullableReadWritePojoWeavingProcessor implements TweedPojoWeavi if (definedEntryReader != null) { readWriteExtension.setEntryReader( configEntry, - new TweedEntryReaderWriterImpls.NullableReader<>(definedEntryReader) + new TweedEntryReaderWriterImpls.FixedNullableReader<>(definedEntryReader) ); } val definedEntryWriter = readWriteExtension.getDefinedEntryWriter(configEntry); if (definedEntryWriter != null) { readWriteExtension.setEntryWriter( configEntry, - new TweedEntryReaderWriterImpls.NullableWriter<>(definedEntryWriter) + new TweedEntryReaderWriterImpls.FixedNullableWriter<>(definedEntryWriter) ); } } diff --git a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoReadWriteNullability.java b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoReadWriteNullability.java index 6ccaf96..3a40893 100644 --- a/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoReadWriteNullability.java +++ b/tweed5/weaver-pojo-serde-extension/src/main/java/de/siphalor/tweed5/weaver/pojoext/serde/api/nullable/AutoReadWriteNullability.java @@ -1,5 +1,9 @@ package de.siphalor.tweed5.weaver.pojoext.serde.api.nullable; +/** + * @see AutoNullableReadWriteBehavior + */ +@Deprecated public enum AutoReadWriteNullability { NON_NULL, NULLABLE, 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 7d397b8..14f3996 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 @@ -4,6 +4,7 @@ import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.core.api.entry.CollectionConfigEntry; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.api.entry.NullableConfigEntry; 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; @@ -12,9 +13,11 @@ 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.NullablePojoWeaver; import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; import lombok.Data; import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -24,6 +27,7 @@ import java.util.List; import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.write; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.InstanceOfAssertFactories.type; @NullUnmarked class AutoReadWritePojoWeavingProcessorTest { @@ -50,7 +54,11 @@ class AutoReadWritePojoWeavingProcessorTest { assertReaderAndWriter(primitiveIntEntry, TweedEntryReaderWriterImpls.INT_READER_WRITER); var boxedLongEntry = rootEntry.subEntries().get("boxedLong"); - assertReaderAndWriter(boxedLongEntry, TweedEntryReaderWriterImpls.LONG_READER_WRITER); + assertReaderAndWriter(boxedLongEntry, TweedEntryReaderWriterImpls.NULLABLE_READER_WRITER); + assertThat(boxedLongEntry).asInstanceOf(type(NullableConfigEntry.class)) + .satisfies(nullableEntry -> assertReaderAndWriter( + nullableEntry.nonNullEntry(), TweedEntryReaderWriterImpls.LONG_READER_WRITER + )); var stringEntry = rootEntry.subEntries().get("string"); assertReaderAndWriter(stringEntry, TweedEntryReaderWriterImpls.STRING_READER_WRITER); @@ -115,6 +123,7 @@ class AutoReadWritePojoWeavingProcessorTest { } @PojoWeaving(extensions = ReadWriteExtension.class) + @PojoWeavingExtension(NullablePojoWeaver.class) @DefaultWeavingExtensions @PojoWeavingExtension(AutoReadWritePojoWeavingProcessor.class) @DefaultReadWriteMappings @@ -122,7 +131,7 @@ class AutoReadWritePojoWeavingProcessorTest { @Data public static class AnnotatedConfig { private int primitiveInt; - private Long boxedLong; + private @Nullable Long boxedLong; private String string; private List<@CompoundWeaving Nested> nesteds; } 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 88c3a9c..f63d68a 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 @@ -85,22 +85,22 @@ class AutoNullableReadWritePojoWeavingProcessorTest { private void assertNullableReader(ConfigEntry entry) { assertThat(readWriteExtension.getDefinedEntryReader(entry)) - .isInstanceOf(TweedEntryReaderWriterImpls.NullableReader.class); + .isInstanceOf(TweedEntryReaderWriterImpls.FixedNullableReader.class); } private void assertNonNullableReader(ConfigEntry entry) { assertThat(readWriteExtension.getDefinedEntryReader(entry)) - .isNotInstanceOf(TweedEntryReaderWriterImpls.NullableReader.class); + .isNotInstanceOf(TweedEntryReaderWriterImpls.FixedNullableReader.class); } private void assertNullableWriter(ConfigEntry entry) { assertThat(readWriteExtension.getDefinedEntryWriter(entry)) - .isInstanceOf(TweedEntryReaderWriterImpls.NullableWriter.class); + .isInstanceOf(TweedEntryReaderWriterImpls.FixedNullableWriter.class); } private void assertNonNullableWriter(ConfigEntry entry) { assertThat(readWriteExtension.getDefinedEntryWriter(entry)) - .isNotInstanceOf(TweedEntryReaderWriterImpls.NullableWriter.class); + .isNotInstanceOf(TweedEntryReaderWriterImpls.FixedNullableWriter.class); } @SneakyThrows diff --git a/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/NullablePojoWeaver.java b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/NullablePojoWeaver.java new file mode 100644 index 0000000..50456b5 --- /dev/null +++ b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/api/weaving/NullablePojoWeaver.java @@ -0,0 +1,66 @@ +package de.siphalor.tweed5.weaver.pojo.api.weaving; + +import de.siphalor.tweed5.core.api.entry.ConfigEntry; +import de.siphalor.tweed5.core.impl.entry.NullableConfigEntryImpl; +import de.siphalor.tweed5.patchwork.api.Patchwork; +import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; +import de.siphalor.tweed5.typeutils.api.type.ActualType; +import lombok.AllArgsConstructor; +import lombok.Setter; +import org.jspecify.annotations.Nullable; + +import java.lang.annotation.Annotation; +import java.util.Locale; + +public class NullablePojoWeaver implements TweedPojoWeavingExtension { + private @Nullable PatchworkPartAccess customDataAccess; + + @Override + public void setup(SetupContext context) { + customDataAccess = context.registerWeavingContextExtensionData(CustomData.class); + } + + @Override + public void beforeWeaveEntry(ActualType valueType, Patchwork extensionsData, ProtoWeavingContext context) { + assert customDataAccess != null; + + Boolean nullable = null; + for (Annotation annotation : valueType.getAnnotations()) { + String annotationName = annotation.annotationType().getSimpleName().toLowerCase(Locale.ROOT); + switch (annotationName) { + case "nullable": + nullable = true; + break; + case "nonnull": + case "notnull": + nullable = false; + break; + } + } + + extensionsData.set(customDataAccess, new CustomData(Boolean.TRUE.equals(nullable))); + } + + @Override + public @Nullable ConfigEntry weaveEntry(ActualType valueType, WeavingContext context) { + assert customDataAccess != null; + + CustomData customData = context.extensionsData().get(customDataAccess); + if (customData == null || !customData.nullable) { + return null; + } + + customData.nullable(false); + return new NullableConfigEntryImpl<>( + context.configContainer(), + valueType.declaredType(), + context.weavePseudoEntry(context, "nonNull", context.extensionsData()) + ); + } + + @AllArgsConstructor + @Setter + private static class CustomData { + private boolean nullable; + } +} 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 6a8d30b..ead29bf 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 @@ -20,6 +20,8 @@ public class WeavingContext { WeavingFn weavingFunction; ConfigContainer configContainer; String[] path; + ActualType valueType; + boolean isPseudo; Patchwork extensionsData; AnnotatedElement annotations; @@ -27,7 +29,12 @@ public class WeavingContext { return weavingFunction.weaveEntry(valueType, extensionsData, context); } + public ConfigEntry weavePseudoEntry(WeavingContext parentContext, String pseudoEntryName, Patchwork extensionsData) { + return weavingFunction.weavePseudoEntry(parentContext, pseudoEntryName, extensionsData); + } + public interface WeavingFn { ConfigEntry weaveEntry(ActualType valueType, Patchwork extensionsData, ProtoWeavingContext context); + ConfigEntry weavePseudoEntry(WeavingContext parentContext, String pseudoEntryName, Patchwork extensionsData); } } diff --git a/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java index 19d4884..da7bb1c 100644 --- a/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java +++ b/tweed5/weaver-pojo/src/main/java/de/siphalor/tweed5/weaver/pojo/impl/entry/StaticPojoCompoundConfigEntry.java @@ -79,7 +79,7 @@ public class StaticPojoCompoundConfigEntry extends BaseConfigEntry impleme } @Override - public void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value) { + public void visitInOrder(ConfigEntryValueVisitor visitor, T value) { if (visitor.enterStructuredEntry(this, value)) { subEntries.forEach((key, entry) -> { if (visitor.enterStructuredSubEntry(key, key)) { 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 4ea058d..ac8cbda 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 @@ -33,6 +33,21 @@ public class TweedPojoWeaverBootstrapper { private final AnnotatedElement pojoAnnotations; private final ConfigContainer configContainer; private final Collection weavingExtensions; + private final WeavingContext.WeavingFn weavingContextFn = new WeavingContext.WeavingFn() { + @Override + public ConfigEntry weaveEntry( + ActualType valueType, + Patchwork extensionsData, + ProtoWeavingContext context + ) { + return TweedPojoWeaverBootstrapper.this.weaveEntry(valueType, extensionsData, context); + } + + @Override + public ConfigEntry weavePseudoEntry(WeavingContext parentContext, String pseudoEntryName, Patchwork extensionsData) { + return TweedPojoWeaverBootstrapper.this.weavePseudoEntry(parentContext, pseudoEntryName, extensionsData); + } + }; @Nullable private PatchworkFactory weavingExtensionsPatchworkFactory; @@ -119,18 +134,19 @@ public class TweedPojoWeaverBootstrapper { } private ConfigEntry weaveEntry( - ActualType dataClass, + ActualType valueType, Patchwork extensionsData, ProtoWeavingContext protoContext ) { extensionsData = extensionsData.copy(); - runBeforeWeaveHooks(dataClass, extensionsData, protoContext); + runBeforeWeaveHooks(valueType, extensionsData, protoContext); WeavingContext context = WeavingContext.builder() .parent(protoContext.parent()) - .weavingFunction(this::weaveEntry) + .weavingFunction(weavingContextFn) .configContainer(configContainer) + .valueType(valueType) .path(protoContext.path()) .extensionsData(extensionsData) .annotations(new AnnotationInheritanceAwareAnnotatedElement(protoContext.annotations())) @@ -138,17 +154,70 @@ public class TweedPojoWeaverBootstrapper { for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { try { - ConfigEntry configEntry = weavingExtension.weaveEntry(dataClass, context); + ConfigEntry configEntry = weavingExtension.weaveEntry(valueType, context); if (configEntry != null) { - runAfterWeaveHooks(dataClass, configEntry, context); + runAfterWeaveHooks(valueType, configEntry, context); return configEntry; } } catch (Exception e) { - log.error("Failed to run Tweed POJO weaver (" + weavingExtension.getClass().getName() + ")", e); + log.error( + "Failed to run Tweed POJO weaver (" + weavingExtension.getClass().getName() + ")" + + " for entry at " + Arrays.toString(context.path()), + e + ); } } - throw new PojoWeavingException("Failed to weave " + dataClass + ": No matching weavers found"); + throw new PojoWeavingException( + "Failed to weave entry for " + valueType + " at " + Arrays.toString(context.path()) + + ": No matching weavers found" + ); + } + + private ConfigEntry weavePseudoEntry( + WeavingContext parentContext, + String pseudoEntryName, + Patchwork extensionsData + ) { + extensionsData = extensionsData.copy(); + + //noinspection unchecked + ActualType valueType = (ActualType) parentContext.valueType(); + + String[] path = Arrays.copyOf(parentContext.path(), parentContext.path().length + 1); + path[path.length - 1] = ":" + pseudoEntryName; + + WeavingContext context = WeavingContext.builder() + .parent(parentContext) + .weavingFunction(weavingContextFn) + .configContainer(configContainer) + .path(path) + .valueType(parentContext.valueType()) + .isPseudo(true) + .extensionsData(extensionsData) + .annotations(parentContext.annotations()) + .build(); + + for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { + try { + ConfigEntry configEntry = weavingExtension.weaveEntry(valueType, context); + if (configEntry != null) { + runAfterWeaveHooks(valueType, configEntry, context); + return configEntry; + } + } catch (Exception e) { + log.error( + "Failed to run Tweed POJO weaver (" + weavingExtension.getClass().getName() + ")" + + " for pseudo entry at " + Arrays.toString(context.path()), + e + ); + } + } + + throw new PojoWeavingException( + "Failed to weave pseudo entry for " + valueType + " at " + Arrays.toString(context.path()) + + ": No matching weavers found" + ); } private void runBeforeWeaveHooks( 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 bcd61cf..c111315 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 @@ -7,10 +7,11 @@ 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.jspecify.annotations.NullUnmarked; +import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.Test; import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isCompoundEntryForClassWith; import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isSimpleEntryForClass; @@ -18,7 +19,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @SuppressWarnings("unused") -@NullUnmarked class CompoundPojoWeaverTest { @Test @@ -30,27 +30,40 @@ class CompoundPojoWeaverTest { PatchworkFactory weavingContextExtensionDataFactory = weavingContextExtensionDataFactoryBuilder.build(); + AtomicReference weavingFunction = new AtomicReference<>(); + weavingFunction.set(new WeavingContext.WeavingFn() { + @Override + 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(Objects.requireNonNull(weavingFunction.get())) + .build(); + return Objects.requireNonNullElseGet( + compoundWeaver.weaveEntry(valueType, context), + () -> new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType()) + ); + } + + @Override + public ConfigEntry weavePseudoEntry( + WeavingContext parentContext, + String pseudoEntryName, + Patchwork extensionsData + ) { + assert false; + return null; + } + }); + WeavingContext weavingContext = WeavingContext.builder() - .weavingFunction(new WeavingContext.WeavingFn() { - @Override - 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()) - ); - } - }) + .weavingFunction(Objects.requireNonNull(weavingFunction.get())) .extensionsData(weavingContextExtensionDataFactory.create()) .annotations(Compound.class) .path(new String[0])