[core, weaver-pojo] Introduce NullableConfigEntry and pseudo entries during weaving

This commit is contained in:
2025-10-28 19:15:29 +01:00
parent ffbecb9158
commit 370324668d
21 changed files with 418 additions and 90 deletions

View File

@@ -2,7 +2,6 @@ package de.siphalor.tweed5.core.api.entry;
import de.siphalor.tweed5.core.api.container.ConfigContainer; import de.siphalor.tweed5.core.api.container.ConfigContainer;
import de.siphalor.tweed5.patchwork.api.Patchwork; import de.siphalor.tweed5.patchwork.api.Patchwork;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -26,7 +25,7 @@ public interface ConfigEntry<T extends @Nullable Object> {
} }
void visitInOrder(ConfigEntryVisitor visitor); 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);
} }

View File

@@ -0,0 +1,17 @@
package de.siphalor.tweed5.core.api.entry;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public interface NullableConfigEntry<T extends @Nullable Object> extends StructuredConfigEntry<T> {
String NON_NULL_KEY = ":nonNull";
@Override
default NullableConfigEntry<T> apply(Consumer<ConfigEntry<T>> function) {
StructuredConfigEntry.super.apply(function);
return this;
}
ConfigEntry<T> nonNullEntry();
}

View File

@@ -16,7 +16,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.*; import java.util.*;
public class DefaultConfigContainer<T> implements ConfigContainer<T> { public class DefaultConfigContainer<T extends @Nullable Object> implements ConfigContainer<T> {
@Getter @Getter
private ConfigContainerSetupPhase setupPhase = ConfigContainerSetupPhase.EXTENSIONS_SETUP; private ConfigContainerSetupPhase setupPhase = ConfigContainerSetupPhase.EXTENSIONS_SETUP;
private final Set<Class<? extends TweedExtension>> requestedExtensions = new HashSet<>(); private final Set<Class<? extends TweedExtension>> requestedExtensions = new HashSet<>();

View File

@@ -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<T extends @Nullable Object> extends BaseConfigEntry<T> implements NullableConfigEntry<T> {
private final ConfigEntry<T> nonNullEntry;
public NullableConfigEntryImpl(
ConfigContainer<?> container,
Class<T> valueClass,
ConfigEntry<T> nonNullEntry
) {
super(container, valueClass);
this.nonNullEntry = nonNullEntry;
}
@Override
public Map<String, ConfigEntry<?>> 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;
}
}

View File

@@ -1,10 +1,12 @@
package de.siphalor.tweed5.defaultextensions.comment.impl; package de.siphalor.tweed5.defaultextensions.comment.impl;
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; 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.entry.SimpleConfigEntry;
import de.siphalor.tweed5.core.api.extension.TweedExtension; import de.siphalor.tweed5.core.api.extension.TweedExtension;
import de.siphalor.tweed5.core.api.middleware.Middleware; import de.siphalor.tweed5.core.api.middleware.Middleware;
import de.siphalor.tweed5.core.impl.DefaultConfigContainer; 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.SimpleConfigEntryImpl;
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl; import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
@@ -36,7 +38,7 @@ class CommentExtensionImplTest {
private DefaultConfigContainer<@NonNull Map<String, Object>> configContainer; private DefaultConfigContainer<@NonNull Map<String, Object>> configContainer;
private CompoundConfigEntry<Map<String, Object>> rootEntry; private CompoundConfigEntry<Map<String, Object>> rootEntry;
private SimpleConfigEntry<Integer> intEntry; private NullableConfigEntry<Integer> intEntry;
private SimpleConfigEntry<String> stringEntry; private SimpleConfigEntry<String> stringEntry;
private SimpleConfigEntry<Long> noCommentEntry; private SimpleConfigEntry<Long> noCommentEntry;
@@ -49,9 +51,13 @@ class CommentExtensionImplTest {
configContainer.registerExtensions(extraExtensions); configContainer.registerExtensions(extraExtensions);
configContainer.finishExtensionSetup(); configContainer.finishExtensionSetup();
intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) intEntry = new NullableConfigEntryImpl<>(
configContainer, Integer.class,
new SimpleConfigEntryImpl<>(configContainer, Integer.class)
.apply(entryReaderWriter(intReaderWriter())) .apply(entryReaderWriter(intReaderWriter()))
.apply(baseComment("It is an integer")); .apply(baseComment("It is an integer")))
.apply(entryReaderWriter(nullableReaderWriter()))
.apply(baseComment("This is nullable"));
stringEntry = new SimpleConfigEntryImpl<>(configContainer, String.class) stringEntry = new SimpleConfigEntryImpl<>(configContainer, String.class)
.apply(entryReaderWriter(stringReaderWriter())) .apply(entryReaderWriter(stringReaderWriter()))
.apply(baseComment("It is a string")); .apply(baseComment("It is a string"));
@@ -80,7 +86,7 @@ class CommentExtensionImplTest {
configContainer.initialize(); configContainer.initialize();
CommentExtension commentExtension = configContainer.extension(CommentExtension.class).orElseThrow(); 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)); assertEquals("It is a string", commentExtension.getFullComment(stringEntry));
assertNull(commentExtension.getFullComment(noCommentEntry)); assertNull(commentExtension.getFullComment(noCommentEntry));
} }
@@ -91,7 +97,7 @@ class CommentExtensionImplTest {
configContainer.initialize(); configContainer.initialize();
CommentExtension commentExtension = configContainer.extension(CommentExtension.class).orElseThrow(); 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:\nIt is a string\nEND", commentExtension.getFullComment(stringEntry));
assertEquals("The comment is:\n\nEND", commentExtension.getFullComment(noCommentEntry)); assertEquals("The comment is:\n\nEND", commentExtension.getFullComment(noCommentEntry));
} }
@@ -120,6 +126,7 @@ class CommentExtensionImplTest {
// This is the root value. // This is the root value.
// It is the topmost value in the tree. // It is the topmost value in the tree.
{ {
\t// This is nullable
\t// It is an integer \t// It is an integer
\tint: 123 \tint: 123
\t// It is a string \t// It is a string

View File

@@ -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.api.entry.ConfigEntry;
import de.siphalor.tweed5.core.impl.DefaultConfigContainer; import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
import de.siphalor.tweed5.core.impl.entry.CollectionConfigEntryImpl; 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.SimpleConfigEntryImpl;
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl; import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension; import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
@@ -51,28 +52,30 @@ class PatchExtensionImplTest {
var int1Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) var int1Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class)
.apply(entryReaderWriter(intReaderWriter())); .apply(entryReaderWriter(intReaderWriter()));
var int2Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) var int2Entry = new NullableConfigEntryImpl<>(
.apply(entryReaderWriter( configContainer, Integer.class,
nullableReader(intReaderWriter()), new SimpleConfigEntryImpl<>(configContainer, Integer.class)
nullableWriter(intReaderWriter()) .apply(entryReaderWriter(intReaderWriter())));
)); var listEntry = new NullableConfigEntryImpl<>(
var listEntry = new CollectionConfigEntryImpl<>( configContainer,
(Class<List<Integer>>)(Class) List.class,
new CollectionConfigEntryImpl<>(
configContainer, configContainer,
(Class<List<Integer>>)(Class) List.class, (Class<List<Integer>>)(Class) List.class,
ArrayList::new, ArrayList::new,
new SimpleConfigEntryImpl<>(configContainer, Integer.class) new SimpleConfigEntryImpl<>(configContainer, Integer.class)
.apply(entryReaderWriter(intReaderWriter())) .apply(entryReaderWriter(intReaderWriter()))
) ).apply(entryReaderWriter(collectionReaderWriter()))
.apply(entryReaderWriter( ).apply(entryReaderWriter(nullableReaderWriter()));
nullableReader(collectionReaderWriter()),
nullableWriter(collectionReaderWriter())
));
var nestedInt1Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) var nestedInt1Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class)
.apply(entryReaderWriter(intReaderWriter())); .apply(entryReaderWriter(intReaderWriter()));
var nestedInt2Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class) var nestedInt2Entry = new SimpleConfigEntryImpl<>(configContainer, Integer.class)
.apply(entryReaderWriter(intReaderWriter())); .apply(entryReaderWriter(intReaderWriter()));
var compoundEntry = new StaticMapCompoundConfigEntryImpl<>( var compoundEntry = new NullableConfigEntryImpl<>(
configContainer,
(Class<Map<String, Object>>)(Class) Map.class,
new StaticMapCompoundConfigEntryImpl<>(
configContainer, configContainer,
(Class<Map<String, Object>>)(Class) Map.class, (Class<Map<String, Object>>)(Class) Map.class,
HashMap::new, HashMap::new,
@@ -80,11 +83,8 @@ class PatchExtensionImplTest {
entry("int1", nestedInt1Entry), entry("int1", nestedInt1Entry),
entry("int2", nestedInt2Entry) entry("int2", nestedInt2Entry)
)) ))
) ).apply(entryReaderWriter(compoundReaderWriter()))
.apply(entryReaderWriter( ).apply(entryReaderWriter(nullableReaderWriter()));
nullableReader(compoundReaderWriter()),
nullableWriter(compoundReaderWriter())
));
rootEntry = new StaticMapCompoundConfigEntryImpl<>( rootEntry = new StaticMapCompoundConfigEntryImpl<>(
configContainer, configContainer,

View File

@@ -1,6 +1,5 @@
package de.siphalor.tweed5.defaultextensions.pather.api; 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.TweedDataReader;
import de.siphalor.tweed5.dataapi.api.TweedDataToken; import de.siphalor.tweed5.dataapi.api.TweedDataToken;
import de.siphalor.tweed5.dataapi.api.TweedDataTokens; import de.siphalor.tweed5.dataapi.api.TweedDataTokens;

View File

@@ -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.CollectionConfigEntry;
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry; 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.TweedEntryReader;
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter; import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import java.util.Collection; import java.util.Collection;
@@ -51,12 +53,25 @@ public class TweedEntryReaderWriters {
return (TweedEntryReaderWriter<T, @NonNull ConfigEntry<T>>)(TweedEntryReaderWriter) TweedEntryReaderWriterImpls.ENUM_READER_WRITER; return (TweedEntryReaderWriter<T, @NonNull ConfigEntry<T>>)(TweedEntryReaderWriter) TweedEntryReaderWriterImpls.ENUM_READER_WRITER;
} }
public static <T, C extends ConfigEntry<T>> TweedEntryReader<T, C> nullableReader(TweedEntryReader<T, C> delegate) { public static <T extends @Nullable Object> TweedEntryReaderWriter<T, NullableConfigEntry<T>> nullableReaderWriter() {
return new TweedEntryReaderWriterImpls.NullableReader<>(delegate); //noinspection unchecked
return (TweedEntryReaderWriter<T, @NonNull NullableConfigEntry<T>>) (TweedEntryReaderWriter<?, ?>) TweedEntryReaderWriterImpls.NULLABLE_READER_WRITER;
} }
/**
* @deprecated You probably want to use {@link #nullableReaderWriter()} instead.
*/
@Deprecated
public static <T, C extends ConfigEntry<T>> TweedEntryReader<T, C> nullableReader(TweedEntryReader<T, C> delegate) {
return new TweedEntryReaderWriterImpls.FixedNullableReader<>(delegate);
}
/**
* @deprecated You probably want to use {@link #nullableReaderWriter()} instead.
*/
@Deprecated
public static <T, C extends ConfigEntry<T>> TweedEntryWriter<T, C> nullableWriter(TweedEntryWriter<T, C> delegate) { public static <T, C extends ConfigEntry<T>> TweedEntryWriter<T, C> nullableWriter(TweedEntryWriter<T, C> delegate) {
return new TweedEntryReaderWriterImpls.NullableWriter<>(delegate); return new TweedEntryReaderWriterImpls.FixedNullableWriter<>(delegate);
} }
public static <T, C extends Collection<T>> TweedEntryReaderWriter<C, CollectionConfigEntry<T, C>> collectionReaderWriter() { public static <T, C extends Collection<T>> TweedEntryReaderWriter<C, CollectionConfigEntry<T, C>> collectionReaderWriter() {

View File

@@ -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.TweedEntryReader;
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter; import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
import de.siphalor.tweed5.data.extension.api.TweedReaderWriterProvider; 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.*; 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.registerWriterFactory("tweed5.string", new StaticReaderWriterFactory<>(stringReaderWriter()));
context.registerReaderFactory("tweed5.enum", new StaticReaderWriterFactory<>(enumReaderWriter())); context.registerReaderFactory("tweed5.enum", new StaticReaderWriterFactory<>(enumReaderWriter()));
context.registerWriterFactory("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.registerReaderFactory("tweed5.collection", new StaticReaderWriterFactory<>(collectionReaderWriter()));
context.registerWriterFactory("tweed5.collection", new StaticReaderWriterFactory<>(collectionReaderWriter())); context.registerWriterFactory("tweed5.collection", new StaticReaderWriterFactory<>(collectionReaderWriter()));
context.registerReaderFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter())); context.registerReaderFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter()));
context.registerWriterFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter())); context.registerWriterFactory("tweed5.compound", new StaticReaderWriterFactory<>(compoundReaderWriter()));
}
context.registerReaderFactory("tweed5.nullable", delegateReaders -> { @RequiredArgsConstructor
if (delegateReaders.length != 1) { private static class NullableReaderWriterFactory<T> implements ReaderWriterFactory<T> {
throw new IllegalArgumentException("Nullable reader requires a single delegate argument, got " + delegateReaders.length); private final Function<T, T> delegateBasedFactory;
}
return nullableReader(delegateReaders[0]); @Override
}); public T create(T... delegateReaderWriters) {
context.registerWriterFactory("tweed5.nullable", delegateWriters -> { if (delegateReaderWriters.length == 0) {
if (delegateWriters.length != 1) { //noinspection unchecked
throw new IllegalArgumentException("Nullable writer requires a single delegate argument, got " + delegateWriters.length); return (T) TweedEntryReaderWriters.nullableReaderWriter();
} } else if (delegateReaderWriters.length == 1) {
return nullableWriter(delegateWriters[0]); return delegateBasedFactory.apply(delegateReaderWriters[0]);
}); } else {
throw new IllegalArgumentException("Nullable readers and writers may have only one or zero delegates");
}
}
} }
} }

View File

@@ -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.CollectionConfigEntry;
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry; 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.*;
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter; import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
import de.siphalor.tweed5.dataapi.api.*; import de.siphalor.tweed5.dataapi.api.*;
@@ -28,13 +29,48 @@ public class TweedEntryReaderWriterImpls {
public static final TweedEntryReaderWriter<String, ConfigEntry<String>> STRING_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsString, TweedDataVisitor::visitString); public static final TweedEntryReaderWriter<String, ConfigEntry<String>> STRING_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsString, TweedDataVisitor::visitString);
public static final TweedEntryReaderWriter<Enum<?>, ConfigEntry<Enum<?>>> ENUM_READER_WRITER = new EnumReaderWriter<>(); public static final TweedEntryReaderWriter<Enum<?>, ConfigEntry<Enum<?>>> ENUM_READER_WRITER = new EnumReaderWriter<>();
public static final TweedEntryReaderWriter<Object, NullableConfigEntry<Object>> NULLABLE_READER_WRITER = new NullableReaderWriter<>();
public static final TweedEntryReaderWriter<Collection<Object>, CollectionConfigEntry<Object, Collection<Object>>> COLLECTION_READER_WRITER = new CollectionReaderWriter<>(); public static final TweedEntryReaderWriter<Collection<Object>, CollectionConfigEntry<Object, Collection<Object>>> COLLECTION_READER_WRITER = new CollectionReaderWriter<>();
public static final TweedEntryReaderWriter<Object, CompoundConfigEntry<Object>> COMPOUND_READER_WRITER = new CompoundReaderWriter<>(); public static final TweedEntryReaderWriter<Object, CompoundConfigEntry<Object>> COMPOUND_READER_WRITER = new CompoundReaderWriter<>();
public static final TweedEntryReaderWriter<Object, ConfigEntry<Object>> NOOP_READER_WRITER = new NoopReaderWriter(); public static final TweedEntryReaderWriter<Object, ConfigEntry<Object>> NOOP_READER_WRITER = new NoopReaderWriter();
public static class NullableReaderWriter<T extends @Nullable Object> implements TweedEntryReaderWriter<T, NullableConfigEntry<T>> {
@Override
public T read(TweedDataReader reader, NullableConfigEntry<T> entry, TweedReadContext context)
throws TweedEntryReadException {
try {
if (reader.peekToken().isNull()) {
reader.readToken();
return null;
}
} catch (TweedDataReadException e) {
throw new TweedEntryReadException(e, context);
}
TweedEntryReader<T, ConfigEntry<T>> nonNullReader = context.readWriteExtension().getReaderChain(entry.nonNullEntry());
return nonNullReader.read(reader, entry.nonNullEntry(), context);
}
@Override
public void write(
TweedDataVisitor writer,
@Nullable T value,
NullableConfigEntry<T> entry,
TweedWriteContext context
) throws TweedEntryWriteException, TweedDataWriteException {
if (value == null) {
writer.visitNull();
} else {
TweedEntryWriter<T, ConfigEntry<T>> nonNullWriter = context.readWriteExtension().getWriterChain(entry.nonNullEntry());
nonNullWriter.write(writer, value, entry.nonNullEntry(), context);
}
}
}
@Deprecated
@RequiredArgsConstructor @RequiredArgsConstructor
public static class NullableReader<T extends @Nullable Object, C extends ConfigEntry<T>> implements TweedEntryReader<T, C> { public static class FixedNullableReader<T extends @Nullable Object, C extends ConfigEntry<T>> implements TweedEntryReader<T, C> {
private final TweedEntryReader<T, C> delegate; private final TweedEntryReader<T, C> delegate;
@Override @Override
@@ -51,8 +87,9 @@ public class TweedEntryReaderWriterImpls {
} }
} }
@Deprecated
@RequiredArgsConstructor @RequiredArgsConstructor
public static class NullableWriter<T extends @Nullable Object, C extends ConfigEntry<T>> implements TweedEntryWriter<T, C> { public static class FixedNullableWriter<T extends @Nullable Object, C extends ConfigEntry<T>> implements TweedEntryWriter<T, C> {
private final TweedEntryWriter<T, C> delegate; private final TweedEntryWriter<T, C> delegate;
@Override @Override

View File

@@ -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.container.ConfigContainer;
import de.siphalor.tweed5.core.api.entry.ConfigEntry; 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.ReadWriteExtension;
import de.siphalor.tweed5.data.extension.api.TweedEntryReader; import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter; import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
import de.siphalor.tweed5.data.extension.api.TweedReaderWriterProvider; 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.data.extension.impl.TweedEntryReaderWriterImpls;
import de.siphalor.tweed5.patchwork.api.Patchwork; import de.siphalor.tweed5.patchwork.api.Patchwork;
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess; import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
@@ -113,6 +115,15 @@ public class AutoReadWritePojoWeavingProcessor implements TweedPojoWeavingExtens
@Override @Override
public <T> void afterWeaveEntry(ActualType<T> valueType, ConfigEntry<T> configEntry, WeavingContext context) { public <T> void afterWeaveEntry(ActualType<T> valueType, ConfigEntry<T> configEntry, WeavingContext context) {
if (configEntry instanceof NullableConfigEntry) {
readWriteExtension.setEntryReaderWriter(
configEntry,
TweedEntryReaderWriters.nullableReaderWriter(),
TweedEntryReaderWriters.nullableReaderWriter()
);
return;
}
assert customDataAccess != null; assert customDataAccess != null;
CustomData customData = context.extensionsData().get(customDataAccess); CustomData customData = context.extensionsData().get(customDataAccess);
if (customData == null || customData.mappings().isEmpty()) { if (customData == null || customData.mappings().isEmpty()) {

View File

@@ -5,6 +5,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/**
* @see AutoNullableReadWritePojoWeavingProcessor
*/
@Deprecated
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface AutoNullableReadWriteBehavior { public @interface AutoNullableReadWriteBehavior {

View File

@@ -17,6 +17,11 @@ import org.jspecify.annotations.Nullable;
import java.lang.annotation.Annotation; 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 { public class AutoNullableReadWritePojoWeavingProcessor implements TweedPojoWeavingExtension {
private final ReadWriteExtension readWriteExtension; private final ReadWriteExtension readWriteExtension;
private @Nullable PatchworkPartAccess<CustomData> customDataAccess; private @Nullable PatchworkPartAccess<CustomData> customDataAccess;
@@ -71,14 +76,14 @@ public class AutoNullableReadWritePojoWeavingProcessor implements TweedPojoWeavi
if (definedEntryReader != null) { if (definedEntryReader != null) {
readWriteExtension.setEntryReader( readWriteExtension.setEntryReader(
configEntry, configEntry,
new TweedEntryReaderWriterImpls.NullableReader<>(definedEntryReader) new TweedEntryReaderWriterImpls.FixedNullableReader<>(definedEntryReader)
); );
} }
val definedEntryWriter = readWriteExtension.getDefinedEntryWriter(configEntry); val definedEntryWriter = readWriteExtension.getDefinedEntryWriter(configEntry);
if (definedEntryWriter != null) { if (definedEntryWriter != null) {
readWriteExtension.setEntryWriter( readWriteExtension.setEntryWriter(
configEntry, configEntry,
new TweedEntryReaderWriterImpls.NullableWriter<>(definedEntryWriter) new TweedEntryReaderWriterImpls.FixedNullableWriter<>(definedEntryWriter)
); );
} }
} }

View File

@@ -1,5 +1,9 @@
package de.siphalor.tweed5.weaver.pojoext.serde.api.nullable; package de.siphalor.tweed5.weaver.pojoext.serde.api.nullable;
/**
* @see AutoNullableReadWriteBehavior
*/
@Deprecated
public enum AutoReadWriteNullability { public enum AutoReadWriteNullability {
NON_NULL, NON_NULL,
NULLABLE, NULLABLE,

View File

@@ -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.CollectionConfigEntry;
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry; import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
import de.siphalor.tweed5.core.api.entry.ConfigEntry; 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.ReadWriteExtension;
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter; import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls; 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.DefaultWeavingExtensions;
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving; import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving;
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension; import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension;
import de.siphalor.tweed5.weaver.pojo.api.weaving.NullablePojoWeaver;
import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper; import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper;
import lombok.Data; import lombok.Data;
import org.jspecify.annotations.NullUnmarked; import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; 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 de.siphalor.tweed5.data.extension.api.ReadWriteExtension.write;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
@NullUnmarked @NullUnmarked
class AutoReadWritePojoWeavingProcessorTest { class AutoReadWritePojoWeavingProcessorTest {
@@ -50,7 +54,11 @@ class AutoReadWritePojoWeavingProcessorTest {
assertReaderAndWriter(primitiveIntEntry, TweedEntryReaderWriterImpls.INT_READER_WRITER); assertReaderAndWriter(primitiveIntEntry, TweedEntryReaderWriterImpls.INT_READER_WRITER);
var boxedLongEntry = rootEntry.subEntries().get("boxedLong"); 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"); var stringEntry = rootEntry.subEntries().get("string");
assertReaderAndWriter(stringEntry, TweedEntryReaderWriterImpls.STRING_READER_WRITER); assertReaderAndWriter(stringEntry, TweedEntryReaderWriterImpls.STRING_READER_WRITER);
@@ -115,6 +123,7 @@ class AutoReadWritePojoWeavingProcessorTest {
} }
@PojoWeaving(extensions = ReadWriteExtension.class) @PojoWeaving(extensions = ReadWriteExtension.class)
@PojoWeavingExtension(NullablePojoWeaver.class)
@DefaultWeavingExtensions @DefaultWeavingExtensions
@PojoWeavingExtension(AutoReadWritePojoWeavingProcessor.class) @PojoWeavingExtension(AutoReadWritePojoWeavingProcessor.class)
@DefaultReadWriteMappings @DefaultReadWriteMappings
@@ -122,7 +131,7 @@ class AutoReadWritePojoWeavingProcessorTest {
@Data @Data
public static class AnnotatedConfig { public static class AnnotatedConfig {
private int primitiveInt; private int primitiveInt;
private Long boxedLong; private @Nullable Long boxedLong;
private String string; private String string;
private List<@CompoundWeaving Nested> nesteds; private List<@CompoundWeaving Nested> nesteds;
} }

View File

@@ -85,22 +85,22 @@ class AutoNullableReadWritePojoWeavingProcessorTest {
private void assertNullableReader(ConfigEntry<?> entry) { private void assertNullableReader(ConfigEntry<?> entry) {
assertThat(readWriteExtension.getDefinedEntryReader(entry)) assertThat(readWriteExtension.getDefinedEntryReader(entry))
.isInstanceOf(TweedEntryReaderWriterImpls.NullableReader.class); .isInstanceOf(TweedEntryReaderWriterImpls.FixedNullableReader.class);
} }
private void assertNonNullableReader(ConfigEntry<?> entry) { private void assertNonNullableReader(ConfigEntry<?> entry) {
assertThat(readWriteExtension.getDefinedEntryReader(entry)) assertThat(readWriteExtension.getDefinedEntryReader(entry))
.isNotInstanceOf(TweedEntryReaderWriterImpls.NullableReader.class); .isNotInstanceOf(TweedEntryReaderWriterImpls.FixedNullableReader.class);
} }
private void assertNullableWriter(ConfigEntry<?> entry) { private void assertNullableWriter(ConfigEntry<?> entry) {
assertThat(readWriteExtension.getDefinedEntryWriter(entry)) assertThat(readWriteExtension.getDefinedEntryWriter(entry))
.isInstanceOf(TweedEntryReaderWriterImpls.NullableWriter.class); .isInstanceOf(TweedEntryReaderWriterImpls.FixedNullableWriter.class);
} }
private void assertNonNullableWriter(ConfigEntry<?> entry) { private void assertNonNullableWriter(ConfigEntry<?> entry) {
assertThat(readWriteExtension.getDefinedEntryWriter(entry)) assertThat(readWriteExtension.getDefinedEntryWriter(entry))
.isNotInstanceOf(TweedEntryReaderWriterImpls.NullableWriter.class); .isNotInstanceOf(TweedEntryReaderWriterImpls.FixedNullableWriter.class);
} }
@SneakyThrows @SneakyThrows

View File

@@ -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<CustomData> customDataAccess;
@Override
public void setup(SetupContext context) {
customDataAccess = context.registerWeavingContextExtensionData(CustomData.class);
}
@Override
public <T> void beforeWeaveEntry(ActualType<T> 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 <T> ConfigEntry<T> weaveEntry(ActualType<T> 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;
}
}

View File

@@ -20,6 +20,8 @@ public class WeavingContext {
WeavingFn weavingFunction; WeavingFn weavingFunction;
ConfigContainer<?> configContainer; ConfigContainer<?> configContainer;
String[] path; String[] path;
ActualType<?> valueType;
boolean isPseudo;
Patchwork extensionsData; Patchwork extensionsData;
AnnotatedElement annotations; AnnotatedElement annotations;
@@ -27,7 +29,12 @@ public class WeavingContext {
return weavingFunction.weaveEntry(valueType, extensionsData, context); return weavingFunction.weaveEntry(valueType, extensionsData, context);
} }
public <T> ConfigEntry<T> weavePseudoEntry(WeavingContext parentContext, String pseudoEntryName, Patchwork extensionsData) {
return weavingFunction.weavePseudoEntry(parentContext, pseudoEntryName, extensionsData);
}
public interface WeavingFn { public interface WeavingFn {
<T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, Patchwork extensionsData, ProtoWeavingContext context); <T> ConfigEntry<T> weaveEntry(ActualType<T> valueType, Patchwork extensionsData, ProtoWeavingContext context);
<T> ConfigEntry<T> weavePseudoEntry(WeavingContext parentContext, String pseudoEntryName, Patchwork extensionsData);
} }
} }

View File

@@ -79,7 +79,7 @@ public class StaticPojoCompoundConfigEntry<T> extends BaseConfigEntry<T> impleme
} }
@Override @Override
public void visitInOrder(ConfigEntryValueVisitor visitor, @Nullable T value) { public void visitInOrder(ConfigEntryValueVisitor visitor, T value) {
if (visitor.enterStructuredEntry(this, value)) { if (visitor.enterStructuredEntry(this, value)) {
subEntries.forEach((key, entry) -> { subEntries.forEach((key, entry) -> {
if (visitor.enterStructuredSubEntry(key, key)) { if (visitor.enterStructuredSubEntry(key, key)) {

View File

@@ -33,6 +33,21 @@ public class TweedPojoWeaverBootstrapper<T> {
private final AnnotatedElement pojoAnnotations; private final AnnotatedElement pojoAnnotations;
private final ConfigContainer<T> configContainer; private final ConfigContainer<T> configContainer;
private final Collection<TweedPojoWeavingExtension> weavingExtensions; private final Collection<TweedPojoWeavingExtension> weavingExtensions;
private final WeavingContext.WeavingFn weavingContextFn = new WeavingContext.WeavingFn() {
@Override
public <U> ConfigEntry<U> weaveEntry(
ActualType<U> valueType,
Patchwork extensionsData,
ProtoWeavingContext context
) {
return TweedPojoWeaverBootstrapper.this.weaveEntry(valueType, extensionsData, context);
}
@Override
public <U> ConfigEntry<U> weavePseudoEntry(WeavingContext parentContext, String pseudoEntryName, Patchwork extensionsData) {
return TweedPojoWeaverBootstrapper.this.weavePseudoEntry(parentContext, pseudoEntryName, extensionsData);
}
};
@Nullable @Nullable
private PatchworkFactory weavingExtensionsPatchworkFactory; private PatchworkFactory weavingExtensionsPatchworkFactory;
@@ -119,18 +134,19 @@ public class TweedPojoWeaverBootstrapper<T> {
} }
private <U> ConfigEntry<U> weaveEntry( private <U> ConfigEntry<U> weaveEntry(
ActualType<U> dataClass, ActualType<U> valueType,
Patchwork extensionsData, Patchwork extensionsData,
ProtoWeavingContext protoContext ProtoWeavingContext protoContext
) { ) {
extensionsData = extensionsData.copy(); extensionsData = extensionsData.copy();
runBeforeWeaveHooks(dataClass, extensionsData, protoContext); runBeforeWeaveHooks(valueType, extensionsData, protoContext);
WeavingContext context = WeavingContext.builder() WeavingContext context = WeavingContext.builder()
.parent(protoContext.parent()) .parent(protoContext.parent())
.weavingFunction(this::weaveEntry) .weavingFunction(weavingContextFn)
.configContainer(configContainer) .configContainer(configContainer)
.valueType(valueType)
.path(protoContext.path()) .path(protoContext.path())
.extensionsData(extensionsData) .extensionsData(extensionsData)
.annotations(new AnnotationInheritanceAwareAnnotatedElement(protoContext.annotations())) .annotations(new AnnotationInheritanceAwareAnnotatedElement(protoContext.annotations()))
@@ -138,17 +154,70 @@ public class TweedPojoWeaverBootstrapper<T> {
for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) { for (TweedPojoWeavingExtension weavingExtension : weavingExtensions) {
try { try {
ConfigEntry<U> configEntry = weavingExtension.weaveEntry(dataClass, context); ConfigEntry<U> configEntry = weavingExtension.weaveEntry(valueType, context);
if (configEntry != null) { if (configEntry != null) {
runAfterWeaveHooks(dataClass, configEntry, context); runAfterWeaveHooks(valueType, configEntry, context);
return configEntry; return configEntry;
} }
} catch (Exception e) { } 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 <U> ConfigEntry<U> weavePseudoEntry(
WeavingContext parentContext,
String pseudoEntryName,
Patchwork extensionsData
) {
extensionsData = extensionsData.copy();
//noinspection unchecked
ActualType<U> valueType = (ActualType<U>) 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<U> 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 <U> void runBeforeWeaveHooks( private <U> void runBeforeWeaveHooks(

View File

@@ -7,10 +7,11 @@ import de.siphalor.tweed5.patchwork.api.Patchwork;
import de.siphalor.tweed5.patchwork.api.PatchworkFactory; import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
import de.siphalor.tweed5.typeutils.api.type.ActualType; import de.siphalor.tweed5.typeutils.api.type.ActualType;
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving; 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 org.junit.jupiter.api.Test;
import java.util.Objects; 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.isCompoundEntryForClassWith;
import static de.siphalor.tweed5.weaver.pojo.test.ConfigEntryAssertions.isSimpleEntryForClass; 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; import static org.mockito.Mockito.mock;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@NullUnmarked
class CompoundPojoWeaverTest { class CompoundPojoWeaverTest {
@Test @Test
@@ -30,8 +30,8 @@ class CompoundPojoWeaverTest {
PatchworkFactory weavingContextExtensionDataFactory = weavingContextExtensionDataFactoryBuilder.build(); PatchworkFactory weavingContextExtensionDataFactory = weavingContextExtensionDataFactoryBuilder.build();
WeavingContext weavingContext = WeavingContext.builder() AtomicReference<WeavingContext.@Nullable WeavingFn> weavingFunction = new AtomicReference<>();
.weavingFunction(new WeavingContext.WeavingFn() { weavingFunction.set(new WeavingContext.WeavingFn() {
@Override @Override
public <T> ConfigEntry<T> weaveEntry( public <T> ConfigEntry<T> weaveEntry(
ActualType<T> valueType, ActualType<T> valueType,
@@ -43,14 +43,27 @@ class CompoundPojoWeaverTest {
.annotations(valueType) .annotations(valueType)
.extensionsData(extensionsData.copy()) .extensionsData(extensionsData.copy())
.path(protoContext.path()) .path(protoContext.path())
.weavingFunction(protoContext.parent()::weaveEntry) .weavingFunction(Objects.requireNonNull(weavingFunction.get()))
.build(); .build();
return Objects.requireNonNullElseGet( return Objects.requireNonNullElseGet(
compoundWeaver.weaveEntry(valueType, context), compoundWeaver.weaveEntry(valueType, context),
() -> new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType()) () -> new SimpleConfigEntryImpl<>(context.configContainer(), valueType.declaredType())
); );
} }
})
@Override
public <T> ConfigEntry<T> weavePseudoEntry(
WeavingContext parentContext,
String pseudoEntryName,
Patchwork extensionsData
) {
assert false;
return null;
}
});
WeavingContext weavingContext = WeavingContext.builder()
.weavingFunction(Objects.requireNonNull(weavingFunction.get()))
.extensionsData(weavingContextExtensionDataFactory.create()) .extensionsData(weavingContextExtensionDataFactory.create())
.annotations(Compound.class) .annotations(Compound.class)
.path(new String[0]) .path(new String[0])