[build] Restructure to composite build
This commit is contained in:
11
tweed5/serde-extension/build.gradle.kts
Normal file
11
tweed5/serde-extension/build.gradle.kts
Normal file
@@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id("de.siphalor.tweed5.base-module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":tweed5-core"))
|
||||
api(project(":tweed5-patchwork"))
|
||||
api(project(":tweed5-serde-api"))
|
||||
|
||||
testImplementation(project(":tweed5-serde-hjson"))
|
||||
}
|
||||
2
tweed5/serde-extension/gradle.properties
Normal file
2
tweed5/serde-extension/gradle.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
module.name = Tweed 5 Serde Extension
|
||||
module.description = A Tweed extension that provides support for reading and writing entries using Tweed's serde API.
|
||||
@@ -0,0 +1,130 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
|
||||
import de.siphalor.tweed5.data.extension.impl.ReadWriteExtensionImpl;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface ReadWriteExtension extends TweedExtension {
|
||||
Class<? extends ReadWriteExtension> DEFAULT = ReadWriteExtensionImpl.class;
|
||||
String EXTENSION_ID = "read-write";
|
||||
|
||||
@Override
|
||||
default String getId() {
|
||||
return EXTENSION_ID;
|
||||
}
|
||||
|
||||
static <T> Consumer<ConfigEntry<T>> entryReaderWriter(
|
||||
TweedEntryReaderWriter<T, ? extends ConfigEntry<T>> entryReaderWriter
|
||||
) {
|
||||
return entryReaderWriter(entryReaderWriter, entryReaderWriter);
|
||||
}
|
||||
|
||||
static <T> Consumer<ConfigEntry<T>> entryReaderWriter(
|
||||
TweedEntryReader<T, ? extends ConfigEntry<T>> entryReader,
|
||||
TweedEntryWriter<T, ? extends ConfigEntry<T>> entryWriter
|
||||
) {
|
||||
return entry -> {
|
||||
ReadWriteExtension extension = entry.container().extension(ReadWriteExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No ReadWriteExtension present"));
|
||||
extension.setEntryReader(entry, entryReader);
|
||||
extension.setEntryWriter(entry, entryWriter);
|
||||
};
|
||||
}
|
||||
|
||||
static <T> Consumer<ConfigEntry<T>> entryReader(TweedEntryReader<T, ? extends ConfigEntry<T>> entryReader) {
|
||||
return entry -> {
|
||||
ReadWriteExtension extension = entry.container().extension(ReadWriteExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No ReadWriteExtension present"));
|
||||
extension.setEntryReader(entry, entryReader);
|
||||
};
|
||||
}
|
||||
|
||||
static <T> Consumer<ConfigEntry<T>> entryWriter(TweedEntryWriter<T, ? extends ConfigEntry<T>> entryWriter) {
|
||||
return entry -> {
|
||||
ReadWriteExtension extension = entry.container().extension(ReadWriteExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No ReadWriteExtension present"));
|
||||
extension.setEntryWriter(entry, entryWriter);
|
||||
};
|
||||
}
|
||||
|
||||
static <T extends @Nullable Object> Function<ConfigEntry<T>, T> read(TweedDataReader reader) {
|
||||
return read(reader, null);
|
||||
}
|
||||
|
||||
static <T extends @Nullable Object> Function<ConfigEntry<T>, T> read(
|
||||
TweedDataReader reader,
|
||||
@Nullable Consumer<Patchwork> contextExtensionsDataCustomizer
|
||||
) {
|
||||
return entry -> {
|
||||
try {
|
||||
ReadWriteExtension extension = entry.container().extension(ReadWriteExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No ReadWriteExtension present"));
|
||||
Patchwork contextExtensionsData = extension.createReadWriteContextExtensionsData();
|
||||
if (contextExtensionsDataCustomizer != null) {
|
||||
contextExtensionsDataCustomizer.accept(contextExtensionsData);
|
||||
}
|
||||
return extension.read(reader, entry, contextExtensionsData);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static <T extends @Nullable Object> Consumer<ConfigEntry<T>> write(TweedDataVisitor writer, T value) {
|
||||
return write(writer, value, null);
|
||||
}
|
||||
|
||||
static <T extends @Nullable Object> Consumer<ConfigEntry<T>> write(
|
||||
TweedDataVisitor writer,
|
||||
T value,
|
||||
@Nullable Consumer<Patchwork> contextExtensionsDataCustomizer
|
||||
) {
|
||||
return entry -> {
|
||||
try {
|
||||
ReadWriteExtension extension = entry.container().extension(ReadWriteExtension.class)
|
||||
.orElseThrow(() -> new IllegalStateException("No ReadWriteExtension present"));
|
||||
Patchwork contextExtensionsData = extension.createReadWriteContextExtensionsData();
|
||||
if (contextExtensionsDataCustomizer != null) {
|
||||
contextExtensionsDataCustomizer.accept(contextExtensionsData);
|
||||
}
|
||||
extension.write(writer, value, entry, contextExtensionsData);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
<T, C extends ConfigEntry<T>> @Nullable TweedEntryReader<T, C> getDefinedEntryReader(ConfigEntry<T> entry);
|
||||
<T, C extends ConfigEntry<T>> @Nullable TweedEntryWriter<T, C> getDefinedEntryWriter(ConfigEntry<T> entry);
|
||||
|
||||
<T, C extends ConfigEntry<T>> void setEntryReaderWriter(
|
||||
ConfigEntry<T> entry,
|
||||
TweedEntryReader<T, C> entryReader,
|
||||
TweedEntryWriter<T, C> entryWriter
|
||||
);
|
||||
<T, C extends ConfigEntry<T>> void setEntryReader(ConfigEntry<T> entry, TweedEntryReader<T, C> entryReader);
|
||||
<T, C extends ConfigEntry<T>> void setEntryWriter(ConfigEntry<T> entry, TweedEntryWriter<T, C> entryWriter);
|
||||
|
||||
Patchwork createReadWriteContextExtensionsData();
|
||||
|
||||
<T extends @Nullable Object> T read(TweedDataReader reader, ConfigEntry<T> entry, Patchwork contextExtensionsData)
|
||||
throws TweedEntryReadException;
|
||||
|
||||
<T extends @Nullable Object> void write(
|
||||
TweedDataVisitor writer,
|
||||
T value,
|
||||
ConfigEntry<T> entry,
|
||||
Patchwork contextExtensionsData
|
||||
) throws TweedEntryWriteException;
|
||||
|
||||
<T, C extends ConfigEntry<T>> TweedEntryReader<T, C> getReaderChain(C entry);
|
||||
<T, C extends ConfigEntry<T>> TweedEntryWriter<T, C> getWriterChain(C entry);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class TweedEntryReadException extends Exception {
|
||||
private final TweedReadContext context;
|
||||
|
||||
public TweedEntryReadException(String message, TweedReadContext context) {
|
||||
super(message);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public TweedEntryReadException(String message, Throwable cause, TweedReadContext context) {
|
||||
super(message, cause);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public TweedEntryReadException(String message, TweedEntryReadException cause) {
|
||||
super(message, cause);
|
||||
this.context = cause.context;
|
||||
}
|
||||
|
||||
public TweedEntryReadException(Throwable cause, TweedReadContext context) {
|
||||
super(cause);
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TweedEntryReader<T extends @Nullable Object, C extends ConfigEntry<T>> {
|
||||
T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class TweedEntryWriteException extends Exception {
|
||||
private final TweedWriteContext context;
|
||||
|
||||
public TweedEntryWriteException(String message, TweedWriteContext context) {
|
||||
super(message);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public TweedEntryWriteException(String message, Throwable cause, TweedWriteContext context) {
|
||||
super(message, cause);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public TweedEntryWriteException(String message, TweedEntryWriteException cause) {
|
||||
super(message, cause);
|
||||
this.context = cause.context;
|
||||
}
|
||||
|
||||
public TweedEntryWriteException(Throwable cause, TweedWriteContext context) {
|
||||
super(cause);
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TweedEntryWriter<T extends @Nullable Object, C extends ConfigEntry<T>> {
|
||||
void write(TweedDataVisitor writer, @Nullable T value, C entry, TweedWriteContext context)
|
||||
throws TweedEntryWriteException, TweedDataWriteException;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
|
||||
public interface TweedReadContext {
|
||||
ReadWriteExtension readWriteExtension();
|
||||
Patchwork extensionsData();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* An interface that allows to register {@link TweedEntryReader}s and {@link TweedEntryWriter}s.
|
||||
* Implementing classes should be Java services, e.g. using {@link com.google.auto.service.AutoService}.
|
||||
*/
|
||||
public interface TweedReaderWriterProvider {
|
||||
void provideReaderWriters(ProviderContext context);
|
||||
|
||||
/**
|
||||
* The context where reader and writer factories may be registered.<br />
|
||||
* The reader and writer ids must be globally unique. It is therefore recommended to scope custom reader and writer ids in your own namespace (e.g. "de.siphalor.custom.blub")
|
||||
* Ids may consist of alphanumeric characters and dots.
|
||||
*/
|
||||
interface ProviderContext {
|
||||
void registerReaderFactory(String id, ReaderWriterFactory<TweedEntryReader<?, ?>> readerFactory);
|
||||
void registerWriterFactory(String id, ReaderWriterFactory<TweedEntryWriter<?, ?>> writerFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory that creates a new reader or writer using delegate readers/writers as its arguments.
|
||||
* @param <T>
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ReaderWriterFactory<T> {
|
||||
T create(T... delegateReaderWriters);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
final class StaticReaderWriterFactory<T> implements ReaderWriterFactory<T> {
|
||||
private final T readerWriter;
|
||||
|
||||
@SafeVarargs
|
||||
@Override
|
||||
public final T create(T... delegateReaderWriters) {
|
||||
if (delegateReaderWriters.length != 0) {
|
||||
throw new IllegalArgumentException("Reader writer factory must not be passed any delegates as arguments");
|
||||
}
|
||||
return readerWriter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
|
||||
public interface TweedWriteContext {
|
||||
ReadWriteExtension readWriteExtension();
|
||||
Patchwork extensionsData();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package de.siphalor.tweed5.data.extension.api.extension;
|
||||
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
|
||||
public interface ReadWriteExtensionSetupContext {
|
||||
<E> PatchworkPartAccess<E> registerReadWriteContextExtensionData(Class<E> extensionDataClass);
|
||||
void registerReaderMiddleware(Middleware<TweedEntryReader<?, ?>> middleware);
|
||||
void registerWriterMiddleware(Middleware<TweedEntryWriter<?, ?>> middleware);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.siphalor.tweed5.data.extension.api.extension;
|
||||
|
||||
public interface ReadWriteRelatedExtension {
|
||||
default void setupReadWriteExtension(ReadWriteExtensionSetupContext context) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.siphalor.tweed5.data.extension.api.readwrite;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
public interface TweedEntryReaderWriter<T extends @Nullable Object, C extends ConfigEntry<T>> extends TweedEntryReader<T, C>, TweedEntryWriter<T, C> {}
|
||||
@@ -0,0 +1,71 @@
|
||||
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.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 java.util.Collection;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class TweedEntryReaderWriters {
|
||||
public static TweedEntryReaderWriter<Boolean, ConfigEntry<Boolean>> booleanReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.BOOLEAN_READER_WRITER;
|
||||
}
|
||||
|
||||
public static TweedEntryReaderWriter<Byte, ConfigEntry<Byte>> byteReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.BYTE_READER_WRITER;
|
||||
}
|
||||
|
||||
public static TweedEntryReaderWriter<Short, ConfigEntry<Short>> shortReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.SHORT_READER_WRITER;
|
||||
}
|
||||
|
||||
public static TweedEntryReaderWriter<Integer, ConfigEntry<Integer>> intReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.INT_READER_WRITER;
|
||||
}
|
||||
|
||||
public static TweedEntryReaderWriter<Long, ConfigEntry<Long>> longReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.LONG_READER_WRITER;
|
||||
}
|
||||
|
||||
public static TweedEntryReaderWriter<Float, ConfigEntry<Float>> floatReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.FLOAT_READER_WRITER;
|
||||
}
|
||||
|
||||
public static TweedEntryReaderWriter<Double, ConfigEntry<Double>> doubleReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.DOUBLE_READER_WRITER;
|
||||
}
|
||||
|
||||
public static TweedEntryReaderWriter<String, ConfigEntry<String>> stringReaderWriter() {
|
||||
return TweedEntryReaderWriterImpls.STRING_READER_WRITER;
|
||||
}
|
||||
|
||||
public static <T extends Enum<T>> TweedEntryReaderWriter<T, ConfigEntry<T>> enumReaderWriter() {
|
||||
//noinspection unchecked,rawtypes
|
||||
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) {
|
||||
return new TweedEntryReaderWriterImpls.NullableReader<>(delegate);
|
||||
}
|
||||
|
||||
public static <T, C extends ConfigEntry<T>> TweedEntryWriter<T, C> nullableWriter(TweedEntryWriter<T, C> delegate) {
|
||||
return new TweedEntryReaderWriterImpls.NullableWriter<>(delegate);
|
||||
}
|
||||
|
||||
public static <T, C extends Collection<T>> TweedEntryReaderWriter<C, CollectionConfigEntry<T, C>> collectionReaderWriter() {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryReaderWriter<C, @NonNull CollectionConfigEntry<T, C>>) (TweedEntryReaderWriter<?, ?>) TweedEntryReaderWriterImpls.COLLECTION_READER_WRITER;
|
||||
}
|
||||
|
||||
public static <T> TweedEntryReaderWriter<T, CompoundConfigEntry<T>> compoundReaderWriter() {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryReaderWriter<T, @NonNull CompoundConfigEntry<T>>) (TweedEntryReaderWriter<?, ?>) TweedEntryReaderWriterImpls.COMPOUND_READER_WRITER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.data.extension.api.readwrite;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,60 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
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 static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
|
||||
@AutoService(TweedReaderWriterProvider.class)
|
||||
public class DefaultTweedEntryReaderWriterImplsProvider implements TweedReaderWriterProvider {
|
||||
@Override
|
||||
public void provideReaderWriters(ProviderContext context) {
|
||||
StaticReaderWriterFactory<TweedEntryReader<?, ?>> booleanReaderFactory = new StaticReaderWriterFactory<>(booleanReaderWriter());
|
||||
StaticReaderWriterFactory<TweedEntryWriter<?, ?>> booleanWriterFactory = new StaticReaderWriterFactory<>(booleanReaderWriter());
|
||||
context.registerReaderFactory("tweed5.bool", booleanReaderFactory);
|
||||
context.registerReaderFactory("tweed5.boolean", booleanReaderFactory);
|
||||
context.registerWriterFactory("tweed5.bool", booleanWriterFactory);
|
||||
context.registerWriterFactory("tweed5.boolean", booleanWriterFactory);
|
||||
context.registerReaderFactory("tweed5.byte", new StaticReaderWriterFactory<>(byteReaderWriter()));
|
||||
context.registerWriterFactory("tweed5.byte", new StaticReaderWriterFactory<>(byteReaderWriter()));
|
||||
context.registerReaderFactory("tweed5.short", new StaticReaderWriterFactory<>(shortReaderWriter()));
|
||||
context.registerWriterFactory("tweed5.short", new StaticReaderWriterFactory<>(shortReaderWriter()));
|
||||
StaticReaderWriterFactory<TweedEntryReader<?, ?>> integerReaderFactory =
|
||||
new StaticReaderWriterFactory<>(intReaderWriter());
|
||||
StaticReaderWriterFactory<TweedEntryWriter<?, ?>> integerWriterFactory =
|
||||
new StaticReaderWriterFactory<>(intReaderWriter());
|
||||
context.registerReaderFactory("tweed5.int", integerReaderFactory);
|
||||
context.registerReaderFactory("tweed5.integer", integerReaderFactory);
|
||||
context.registerWriterFactory("tweed5.int", integerWriterFactory);
|
||||
context.registerWriterFactory("tweed5.integer", integerWriterFactory);
|
||||
context.registerReaderFactory("tweed5.long", new StaticReaderWriterFactory<>(longReaderWriter()));
|
||||
context.registerWriterFactory("tweed5.long", new StaticReaderWriterFactory<>(longReaderWriter()));
|
||||
context.registerReaderFactory("tweed5.float", new StaticReaderWriterFactory<>(floatReaderWriter()));
|
||||
context.registerWriterFactory("tweed5.float", new StaticReaderWriterFactory<>(floatReaderWriter()));
|
||||
context.registerReaderFactory("tweed5.double", new StaticReaderWriterFactory<>(doubleReaderWriter()));
|
||||
context.registerWriterFactory("tweed5.double", new StaticReaderWriterFactory<>(doubleReaderWriter()));
|
||||
context.registerReaderFactory("tweed5.string", new StaticReaderWriterFactory<>(stringReaderWriter()));
|
||||
context.registerWriterFactory("tweed5.string", new StaticReaderWriterFactory<>(stringReaderWriter()));
|
||||
context.registerReaderFactory("tweed5.enum", new StaticReaderWriterFactory<>(enumReaderWriter()));
|
||||
context.registerWriterFactory("tweed5.enum", new StaticReaderWriterFactory<>(enumReaderWriter()));
|
||||
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);
|
||||
}
|
||||
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]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||
import de.siphalor.tweed5.core.api.middleware.DefaultMiddlewareContainer;
|
||||
import de.siphalor.tweed5.core.api.middleware.Middleware;
|
||||
import de.siphalor.tweed5.data.extension.api.*;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkFactory;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||
import lombok.Data;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@AutoService(ReadWriteExtension.class)
|
||||
public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
||||
private final ConfigContainer<?> configContainer;
|
||||
private final PatchworkPartAccess<CustomEntryData> customEntryDataAccess;
|
||||
private DefaultMiddlewareContainer<TweedEntryReader<?, ?>>
|
||||
entryReaderMiddlewareContainer
|
||||
= new DefaultMiddlewareContainer<>();
|
||||
private DefaultMiddlewareContainer<TweedEntryWriter<?, ?>>
|
||||
entryWriterMiddlewareContainer
|
||||
= new DefaultMiddlewareContainer<>();
|
||||
private @Nullable PatchworkFactory readWriteContextPatchworkFactory;
|
||||
|
||||
public ReadWriteExtensionImpl(ConfigContainer<?> configContainer, TweedExtensionSetupContext context) {
|
||||
this.configContainer = configContainer;
|
||||
this.customEntryDataAccess = context.registerEntryExtensionData(CustomEntryData.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extensionsFinalized() {
|
||||
Collection<TweedExtension> extensions = configContainer.extensions();
|
||||
|
||||
PatchworkFactory.Builder readWriteContextPatchworkFactorBuilder = PatchworkFactory.builder();
|
||||
entryReaderMiddlewareContainer = new DefaultMiddlewareContainer<>();
|
||||
entryWriterMiddlewareContainer = new DefaultMiddlewareContainer<>();
|
||||
|
||||
ReadWriteExtensionSetupContext setupContext = new ReadWriteExtensionSetupContext() {
|
||||
@Override
|
||||
public <E> PatchworkPartAccess<E> registerReadWriteContextExtensionData(Class<E> extensionDataClass) {
|
||||
return readWriteContextPatchworkFactorBuilder.registerPart(extensionDataClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerReaderMiddleware(Middleware<TweedEntryReader<?, ?>> middleware) {
|
||||
entryReaderMiddlewareContainer.register(middleware);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerWriterMiddleware(Middleware<TweedEntryWriter<?, ?>> middleware) {
|
||||
entryWriterMiddlewareContainer.register(middleware);
|
||||
}
|
||||
};
|
||||
|
||||
for (TweedExtension extension : extensions) {
|
||||
if (extension instanceof ReadWriteRelatedExtension) {
|
||||
((ReadWriteRelatedExtension) extension).setupReadWriteExtension(setupContext);
|
||||
}
|
||||
}
|
||||
|
||||
readWriteContextPatchworkFactory = readWriteContextPatchworkFactorBuilder.build();
|
||||
|
||||
entryReaderMiddlewareContainer.seal();
|
||||
entryWriterMiddlewareContainer.seal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T, C extends ConfigEntry<T>> TweedEntryReader<T, C> getDefinedEntryReader(ConfigEntry<T> entry) {
|
||||
CustomEntryData customEntryData = entry.extensionsData().get(customEntryDataAccess);
|
||||
if (customEntryData == null) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (TweedEntryReader<T, C>) customEntryData.readerDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T, C extends ConfigEntry<T>> TweedEntryWriter<T, C> getDefinedEntryWriter(ConfigEntry<T> entry) {
|
||||
CustomEntryData customEntryData = entry.extensionsData().get(customEntryDataAccess);
|
||||
if (customEntryData == null) {
|
||||
return null;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (TweedEntryWriter<T, C>) customEntryData.writerDefinition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, C extends ConfigEntry<T>> void setEntryReaderWriter(
|
||||
ConfigEntry<T> entry,
|
||||
TweedEntryReader<T, C> entryReader,
|
||||
TweedEntryWriter<T, C> entryWriter
|
||||
) {
|
||||
CustomEntryData customEntryData = getOrCreateCustomEntryData(entry);
|
||||
customEntryData.readerDefinition(entryReader);
|
||||
customEntryData.writerDefinition(entryWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, C extends ConfigEntry<T>> void setEntryReader(ConfigEntry<T> entry, TweedEntryReader<T, C> entryReader) {
|
||||
getOrCreateCustomEntryData(entry).readerDefinition(entryReader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, C extends ConfigEntry<T>> void setEntryWriter(ConfigEntry<T> entry, TweedEntryWriter<T, C> entryWriter) {
|
||||
getOrCreateCustomEntryData(entry).writerDefinition(entryWriter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initEntry(ConfigEntry<?> configEntry) {
|
||||
CustomEntryData customEntryData = getOrCreateCustomEntryData(configEntry);
|
||||
customEntryData.readerChain(entryReaderMiddlewareContainer.process(customEntryData.readerDefinition()));
|
||||
customEntryData.writerChain(entryWriterMiddlewareContainer.process(customEntryData.writerDefinition()));
|
||||
}
|
||||
|
||||
private CustomEntryData getOrCreateCustomEntryData(ConfigEntry<?> entry) {
|
||||
CustomEntryData entryData = entry.extensionsData().get(customEntryDataAccess);
|
||||
if (entryData == null) {
|
||||
entryData = new CustomEntryData();
|
||||
entry.extensionsData().set(customEntryDataAccess, entryData);
|
||||
}
|
||||
return entryData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Patchwork createReadWriteContextExtensionsData() {
|
||||
assert readWriteContextPatchworkFactory != null;
|
||||
return readWriteContextPatchworkFactory.create();
|
||||
}
|
||||
|
||||
public <T extends @Nullable Object> T read(
|
||||
TweedDataReader reader,
|
||||
ConfigEntry<T> entry,
|
||||
Patchwork contextExtensionsData
|
||||
) throws TweedEntryReadException {
|
||||
TweedReadContext context = new TweedReadWriteContextImpl(this, contextExtensionsData);
|
||||
return getReaderChain(entry).read(reader, entry, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends @Nullable Object> void write(
|
||||
TweedDataVisitor writer,
|
||||
@Nullable T value,
|
||||
ConfigEntry<T> entry,
|
||||
Patchwork contextExtensionsData
|
||||
) throws TweedEntryWriteException {
|
||||
TweedWriteContext context = new TweedReadWriteContextImpl(this, contextExtensionsData);
|
||||
try {
|
||||
getWriterChain(entry).write(writer, value, entry, context);
|
||||
} catch (TweedDataWriteException e) {
|
||||
throw new TweedEntryWriteException("Failed to write entry", e, context);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class CustomEntryData {
|
||||
private TweedEntryReader<?, ?> readerDefinition = TweedEntryReaderWriterImpls.NOOP_READER_WRITER;
|
||||
private TweedEntryWriter<?, ?> writerDefinition = TweedEntryReaderWriterImpls.NOOP_READER_WRITER;
|
||||
private TweedEntryReader<?, ?> readerChain;
|
||||
private TweedEntryWriter<?, ?> writerChain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, C extends ConfigEntry<T>> TweedEntryReader<T, C> getReaderChain(C entry) {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryReader<T, C>) entry.extensionsData().get(customEntryDataAccess).readerChain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T, C extends ConfigEntry<T>> TweedEntryWriter<T, C> getWriterChain(C entry) {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryWriter<T, C>) entry.extensionsData().get(customEntryDataAccess).writerChain();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
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.data.extension.api.*;
|
||||
import de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.val;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class TweedEntryReaderWriterImpls {
|
||||
public static final TweedEntryReaderWriter<Boolean, ConfigEntry<Boolean>> BOOLEAN_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsBoolean, TweedDataVisitor::visitBoolean);
|
||||
public static final TweedEntryReaderWriter<Byte, ConfigEntry<Byte>> BYTE_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsByte, TweedDataVisitor::visitByte);
|
||||
public static final TweedEntryReaderWriter<Short, ConfigEntry<Short>> SHORT_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsShort, TweedDataVisitor::visitShort);
|
||||
public static final TweedEntryReaderWriter<Integer, ConfigEntry<Integer>> INT_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsInt, TweedDataVisitor::visitInt);
|
||||
public static final TweedEntryReaderWriter<Long, ConfigEntry<Long>> LONG_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsLong, TweedDataVisitor::visitLong);
|
||||
public static final TweedEntryReaderWriter<Float, ConfigEntry<Float>> FLOAT_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsFloat, TweedDataVisitor::visitFloat);
|
||||
public static final TweedEntryReaderWriter<Double, ConfigEntry<Double>> DOUBLE_READER_WRITER = new PrimitiveReaderWriter<>(TweedDataToken::readAsDouble, TweedDataVisitor::visitDouble);
|
||||
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<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, ConfigEntry<Object>> NOOP_READER_WRITER = new NoopReaderWriter();
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class NullableReader<T extends @Nullable Object, C extends ConfigEntry<T>> implements TweedEntryReader<T, C> {
|
||||
private final TweedEntryReader<T, C> delegate;
|
||||
|
||||
@Override
|
||||
public T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException {
|
||||
try {
|
||||
if (reader.peekToken().isNull()) {
|
||||
reader.readToken();
|
||||
return null;
|
||||
}
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException(e, context);
|
||||
}
|
||||
return delegate.read(reader, entry, context);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class NullableWriter<T extends @Nullable Object, C extends ConfigEntry<T>> implements TweedEntryWriter<T, C> {
|
||||
private final TweedEntryWriter<T, C> delegate;
|
||||
|
||||
@Override
|
||||
public void write(TweedDataVisitor writer, T value, C entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||
if (value == null) {
|
||||
writer.visitNull();
|
||||
} else {
|
||||
delegate.write(writer, value, entry, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class PrimitiveReaderWriter<T> implements TweedEntryReaderWriter<T, ConfigEntry<T>> {
|
||||
private final PrimitiveReadFunction<T> readerFunction;
|
||||
private final PrimitiveWriteFunction<T> writerFunction;
|
||||
|
||||
@Override
|
||||
public T read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context)
|
||||
throws TweedEntryReadException {
|
||||
try {
|
||||
return readerFunction.read(reader.readToken());
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException(e, context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataVisitor writer, @Nullable T value, ConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||
requireNonNullWriteValue(value, context);
|
||||
writerFunction.write(writer, value);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public static class EnumReaderWriter<T extends Enum<?>> implements TweedEntryReaderWriter<T, ConfigEntry<T>> {
|
||||
@Override
|
||||
public T read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context) throws
|
||||
TweedEntryReadException {
|
||||
try {
|
||||
TweedDataToken token = reader.readToken();
|
||||
assertIsToken(token, TweedDataToken::canReadAsString, "Expected string", context);
|
||||
//noinspection unchecked,rawtypes
|
||||
return (T) Enum.valueOf(((Class) entry.valueClass()), token.readAsString());
|
||||
} catch (TweedDataReadException | IllegalArgumentException e) {
|
||||
throw new TweedEntryReadException(
|
||||
"Failed reading enum value for " + entry.valueClass().getName(),
|
||||
e,
|
||||
context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(
|
||||
TweedDataVisitor writer,
|
||||
@Nullable T value,
|
||||
ConfigEntry<T> entry,
|
||||
TweedWriteContext context
|
||||
) throws TweedEntryWriteException, TweedDataWriteException {
|
||||
requireNonNullWriteValue(value, context);
|
||||
writer.visitString(value.name());
|
||||
}
|
||||
}
|
||||
|
||||
public static class CollectionReaderWriter<T extends @Nullable Object, C extends Collection<T>> implements TweedEntryReaderWriter<C, CollectionConfigEntry<T, C>> {
|
||||
@Override
|
||||
public C read(TweedDataReader reader, CollectionConfigEntry<T, C> entry, TweedReadContext context) throws
|
||||
TweedEntryReadException {
|
||||
TweedDataToken token;
|
||||
try {
|
||||
assertIsToken(reader.readToken(), TweedDataToken::isListStart, "Expected list start", context);
|
||||
token = reader.peekToken();
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException("Failed reading collection start", e, context);
|
||||
}
|
||||
if (token.isListEnd()) {
|
||||
return entry.instantiateCollection(0);
|
||||
}
|
||||
|
||||
ConfigEntry<T> elementEntry = entry.elementEntry();
|
||||
TweedEntryReader<T, ConfigEntry<T>> elementReader = context.readWriteExtension().getReaderChain(elementEntry);
|
||||
|
||||
List<@Nullable T> list = new ArrayList<>(20);
|
||||
while (true) {
|
||||
try {
|
||||
token = reader.peekToken();
|
||||
if (token.isListEnd()) {
|
||||
reader.readToken();
|
||||
break;
|
||||
} else if (token.isListValue()) {
|
||||
list.add(elementReader.read(reader, elementEntry, context));
|
||||
} else {
|
||||
throw new TweedEntryReadException(
|
||||
"Unexpected token " + token + ": expected next list value or list end",
|
||||
context
|
||||
);
|
||||
}
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException(
|
||||
"Failed reading element " + list.size() + " of collection",
|
||||
e,
|
||||
context
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
C result = entry.instantiateCollection(list.size());
|
||||
result.addAll(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataVisitor writer, C value, CollectionConfigEntry<T, C> entry, TweedWriteContext context)
|
||||
throws TweedEntryWriteException, TweedDataWriteException {
|
||||
requireNonNullWriteValue(value, context);
|
||||
|
||||
if (value.isEmpty()) {
|
||||
writer.visitEmptyList();
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigEntry<T> elementEntry = entry.elementEntry();
|
||||
TweedEntryWriter<T, ConfigEntry<T>> elementWriter = context.readWriteExtension().getWriterChain(elementEntry);
|
||||
|
||||
writer.visitListStart();
|
||||
for (T element : value) {
|
||||
elementWriter.write(writer, element, elementEntry, context);
|
||||
}
|
||||
writer.visitListEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CompoundReaderWriter<T> implements TweedEntryReaderWriter<T, CompoundConfigEntry<T>> {
|
||||
@Override
|
||||
public T read(TweedDataReader reader, CompoundConfigEntry<T> entry, TweedReadContext context) throws
|
||||
TweedEntryReadException {
|
||||
try {
|
||||
assertIsToken(reader.readToken(), TweedDataToken::isMapStart, "Expected map start", context);
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException("Failed reading compound start", e, context);
|
||||
}
|
||||
|
||||
Map<String, ConfigEntry<?>> compoundEntries = entry.subEntries();
|
||||
T compoundValue = entry.instantiateCompoundValue();
|
||||
while (true) {
|
||||
try {
|
||||
TweedDataToken token = reader.readToken();
|
||||
if (token.isMapEnd()) {
|
||||
break;
|
||||
} else if (token.isMapEntryKey()) {
|
||||
String key = token.readAsString();
|
||||
|
||||
//noinspection unchecked
|
||||
ConfigEntry<Object> subEntry = (ConfigEntry<Object>) compoundEntries.get(key);
|
||||
if (subEntry == null) {
|
||||
//noinspection DataFlowIssue
|
||||
NOOP_READER_WRITER.read(reader, null, context);
|
||||
continue;
|
||||
}
|
||||
val subEntryReaderChain = context.readWriteExtension().getReaderChain(subEntry);
|
||||
Object subEntryValue = subEntryReaderChain.read(reader, subEntry, context);
|
||||
entry.set(compoundValue, key, subEntryValue);
|
||||
} else {
|
||||
throw new TweedEntryReadException(
|
||||
"Unexpected token " + token + ": Expected map key or map end",
|
||||
context
|
||||
);
|
||||
}
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException("Failed reading compound entry", e, context);
|
||||
}
|
||||
}
|
||||
return compoundValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataVisitor writer, @Nullable T value, CompoundConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||
requireNonNullWriteValue(value, context);
|
||||
|
||||
writer.visitMapStart();
|
||||
|
||||
//noinspection unchecked
|
||||
Map<String, ConfigEntry<Object>> compoundEntries = (Map<String, ConfigEntry<Object>>)(Map<?, ?>) entry.subEntries();
|
||||
for (Map.Entry<String, ConfigEntry<Object>> e : compoundEntries.entrySet()) {
|
||||
String key = e.getKey();
|
||||
ConfigEntry<Object> subEntry = e.getValue();
|
||||
|
||||
TweedEntryWriter<Object, ConfigEntry<Object>> subEntryWriterChain = context.readWriteExtension().getWriterChain(subEntry);
|
||||
|
||||
writer.visitMapEntryKey(key);
|
||||
subEntryWriterChain.write(writer, entry.get(value, key), subEntry, context);
|
||||
}
|
||||
|
||||
writer.visitMapEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NoopReaderWriter implements TweedEntryReaderWriter<@Nullable Object, ConfigEntry<Object>> {
|
||||
@Override
|
||||
public @Nullable Object read(TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context)
|
||||
throws TweedEntryReadException {
|
||||
try {
|
||||
TweedDataToken token = reader.readToken();
|
||||
if (!token.isListStart() && !token.isMapStart()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayDeque<Context> stack = new ArrayDeque<>(20);
|
||||
if (token.isListStart()) {
|
||||
stack.push(Context.LIST);
|
||||
} else if (token.isMapStart()) {
|
||||
stack.push(Context.MAP);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
token = reader.readToken();
|
||||
if (token.isListStart()) {
|
||||
stack.push(Context.LIST);
|
||||
} else if (token.isMapStart()) {
|
||||
stack.push(Context.MAP);
|
||||
} else if (token.isListEnd() || token.isMapEnd()) {
|
||||
stack.pop();
|
||||
}
|
||||
if (stack.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException("Failed skipping through value", e, context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataVisitor writer, @Nullable Object value, ConfigEntry<Object> entry, TweedWriteContext context) throws TweedDataWriteException {
|
||||
writer.visitNull();
|
||||
}
|
||||
|
||||
private enum Context {
|
||||
LIST, MAP,
|
||||
}
|
||||
}
|
||||
|
||||
private interface PrimitiveReadFunction<T extends Object> {
|
||||
T read(TweedDataToken token) throws TweedDataReadException;
|
||||
}
|
||||
|
||||
private interface PrimitiveWriteFunction<T extends Object> {
|
||||
void write(TweedDataVisitor writer, T value) throws TweedDataWriteException;
|
||||
}
|
||||
|
||||
@Contract("null, _ -> fail")
|
||||
private static <T> void requireNonNullWriteValue(
|
||||
@Nullable T value,
|
||||
TweedWriteContext context
|
||||
) throws TweedEntryWriteException {
|
||||
if (value == null) {
|
||||
throw new TweedEntryWriteException("Unable to write null value", context);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertIsToken(
|
||||
TweedDataToken token,
|
||||
Predicate<TweedDataToken> isToken,
|
||||
String description,
|
||||
TweedReadContext context
|
||||
) throws TweedEntryReadException {
|
||||
if (!isToken.test(token)) {
|
||||
throw new TweedEntryReadException("Unexpected token " + token + ": " + description, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedWriteContext;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class TweedReadWriteContextImpl implements TweedReadContext, TweedWriteContext {
|
||||
ReadWriteExtension readWriteExtension;
|
||||
Patchwork extensionsData;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -0,0 +1,123 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
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.SimpleConfigEntry;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.CollectionConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonLexer;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonReader;
|
||||
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static de.siphalor.tweed5.data.extension.api.ReadWriteExtension.entryReaderWriter;
|
||||
import static de.siphalor.tweed5.data.extension.api.readwrite.TweedEntryReaderWriters.*;
|
||||
import static de.siphalor.tweed5.testutils.generic.MapTestUtils.sequencedMap;
|
||||
import static java.util.Map.entry;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ReadWriteExtensionImplTest {
|
||||
private final StringWriter stringWriter = new StringWriter();
|
||||
private final ConfigContainer<Map<String, Object>> configContainer = new DefaultConfigContainer<>();
|
||||
private CompoundConfigEntry<Map<String, Object>> rootEntry;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
configContainer.registerExtension(ReadWriteExtension.DEFAULT);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
SimpleConfigEntry<Integer> intEntry = new SimpleConfigEntryImpl<>(configContainer, Integer.class)
|
||||
.apply(entryReaderWriter(intReaderWriter()));
|
||||
|
||||
CollectionConfigEntry<Boolean, List<Boolean>> listEntry = new CollectionConfigEntryImpl<>(
|
||||
configContainer,
|
||||
(Class<List<Boolean>>) (Class<?>) List.class,
|
||||
ArrayList::new,
|
||||
new SimpleConfigEntryImpl<>(configContainer, Boolean.class)
|
||||
.apply(entryReaderWriter(booleanReaderWriter()))
|
||||
).apply(entryReaderWriter(collectionReaderWriter()));
|
||||
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(
|
||||
configContainer,
|
||||
((Class<Map<String, Object>>) (Class<?>) Map.class),
|
||||
LinkedHashMap::new,
|
||||
sequencedMap(List.of(
|
||||
entry("int", intEntry),
|
||||
entry("list", listEntry)
|
||||
))
|
||||
).apply(entryReaderWriter(compoundReaderWriter()));
|
||||
|
||||
configContainer.attachTree(rootEntry);
|
||||
configContainer.initialize();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void write() {
|
||||
Map<String, Object> value = new HashMap<>();
|
||||
value.put("int", 123);
|
||||
value.put("list", Arrays.asList(true, false, true));
|
||||
|
||||
ReadWriteExtension readWriteExtension = configContainer.extension(ReadWriteExtension.class).orElseThrow();
|
||||
assertDoesNotThrow(() -> readWriteExtension.write(
|
||||
setupWriter(writer -> new HjsonWriter(writer, new HjsonWriter.Options())),
|
||||
value,
|
||||
rootEntry,
|
||||
readWriteExtension.createReadWriteContextExtensionsData()
|
||||
));
|
||||
|
||||
assertThat(stringWriter.toString()).isEqualTo("""
|
||||
{
|
||||
\tint: 123
|
||||
\tlist: [
|
||||
\t\ttrue
|
||||
\t\tfalse
|
||||
\t\ttrue
|
||||
\t]
|
||||
}
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void read() {
|
||||
ReadWriteExtension readWriteExtension = configContainer.extension(ReadWriteExtension.class).orElseThrow();
|
||||
Map<String, Object> result = assertDoesNotThrow(() -> readWriteExtension.read(
|
||||
new HjsonReader(new HjsonLexer(new StringReader("""
|
||||
{
|
||||
\tint: 123
|
||||
\tlist: [
|
||||
\t\ttrue
|
||||
\t\tfalse
|
||||
\t\ttrue
|
||||
\t]
|
||||
}
|
||||
"""))),
|
||||
rootEntry,
|
||||
readWriteExtension.createReadWriteContextExtensionsData()
|
||||
));
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(123, result.get("int"));
|
||||
assertEquals(Arrays.asList(true, false, true), result.get("list"));
|
||||
}
|
||||
|
||||
private TweedDataVisitor setupWriter(Function<Writer, TweedDataVisitor> writerFactory) {
|
||||
return writerFactory.apply(stringWriter);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user