Initial commit
That's a lotta stuff for an initial commit, but well...
This commit is contained in:
7
tweed5-serde-extension/build.gradle.kts
Normal file
7
tweed5-serde-extension/build.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
||||
dependencies {
|
||||
api(project(":tweed5-core"))
|
||||
api(project(":tweed5-patchwork"))
|
||||
api(project(":tweed5-serde-api"))
|
||||
|
||||
testImplementation(project(":tweed5-serde-hjson"))
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
public interface EntryReaderWriterDefinition {
|
||||
TweedEntryReader<?, ?> reader();
|
||||
TweedEntryWriter<?, ?> writer();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
public interface ReadWriteEntryDataExtension {
|
||||
TweedEntryReader<?, ?> entryReaderChain();
|
||||
TweedEntryWriter<?, ?> entryWriterChain();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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.extension.ReadWriteContextExtensionsData;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||
|
||||
public interface ReadWriteExtension extends TweedExtension {
|
||||
ReadWriteContextExtensionsData createReadWriteContextExtensionsData();
|
||||
|
||||
<T> T read(TweedDataReader reader, ConfigEntry<T> entry, ReadWriteContextExtensionsData contextExtensionsData) throws TweedEntryReadException;
|
||||
|
||||
<T> void write(TweedDataWriter writer, T value, ConfigEntry<T> entry, ReadWriteContextExtensionsData contextExtensionsData) throws TweedEntryWriteException;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
public class TweedEntryReadException extends Exception {
|
||||
public TweedEntryReadException() {
|
||||
}
|
||||
|
||||
public TweedEntryReadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TweedEntryReadException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TweedEntryReadException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -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.TweedDataReadException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TweedEntryReader<T, C extends ConfigEntry<T>> {
|
||||
T read(TweedDataReader reader, C entry, TweedReadContext context) throws TweedEntryReadException, TweedDataReadException;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
public class TweedEntryWriteException extends Exception {
|
||||
public TweedEntryWriteException() {
|
||||
}
|
||||
|
||||
public TweedEntryWriteException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TweedEntryWriteException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public TweedEntryWriteException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -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.TweedDataWriteException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TweedEntryWriter<T, C extends ConfigEntry<T>> {
|
||||
void write(TweedDataWriter writer, T value, C entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteContextExtensionsData;
|
||||
|
||||
public interface TweedReadContext {
|
||||
ReadWriteContextExtensionsData extensionsData();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.siphalor.tweed5.data.extension.api;
|
||||
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteContextExtensionsData;
|
||||
|
||||
public interface TweedWriteContext {
|
||||
ReadWriteContextExtensionsData extensionsData();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package de.siphalor.tweed5.data.extension.api.extension;
|
||||
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
|
||||
public interface ReadWriteContextExtensionsData extends Patchwork<ReadWriteContextExtensionsData> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.siphalor.tweed5.data.extension.api.extension;
|
||||
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
|
||||
public interface ReadWriteExtensionSetupContext {
|
||||
<E> RegisteredExtensionData<ReadWriteContextExtensionsData, E> registerReadWriteContextExtensionData(Class<E> extensionDataClass);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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 org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface ReadWriteRelatedExtension {
|
||||
default void setupReadWriteExtension(ReadWriteExtensionSetupContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
default Middleware<TweedEntryReader<?, ?>> entryReaderMiddleware() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
default Middleware<TweedEntryWriter<?, ?>> entryWriterMiddleware() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
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;
|
||||
|
||||
public interface TweedEntryReaderWriter<T, C extends ConfigEntry<T>> extends TweedEntryReader<T, C>, TweedEntryWriter<T, C> {}
|
||||
@@ -0,0 +1,55 @@
|
||||
package de.siphalor.tweed5.data.extension.api.readwrite;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.CoherentCollectionConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.data.extension.impl.TweedEntryReaderWriterImpls;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
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, C extends Collection<T>> TweedEntryReaderWriter<C, CoherentCollectionConfigEntry<T, C>> coherentCollectionReaderWriter() {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryReaderWriter<C, CoherentCollectionConfigEntry<T,C>>)(TweedEntryReaderWriter<?, ?>) TweedEntryReaderWriterImpls.COHERENT_COLLECTION_READER_WRITER;
|
||||
}
|
||||
|
||||
public static <T> TweedEntryReaderWriter<T, CompoundConfigEntry<T>> compoundReaderWriter() {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryReaderWriter<T, CompoundConfigEntry<T>>)(TweedEntryReaderWriter<?, ?>) TweedEntryReaderWriterImpls.COMPOUND_READER_WRITER;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
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.data.extension.api.*;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteContextExtensionsData;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteExtensionSetupContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteRelatedExtension;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReadException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataReader;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||
import de.siphalor.tweed5.patchwork.api.Patchwork;
|
||||
import de.siphalor.tweed5.patchwork.api.PatchworkClassCreator;
|
||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClass;
|
||||
import de.siphalor.tweed5.patchwork.impl.PatchworkClassGenerator;
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ReadWriteExtensionImpl implements ReadWriteExtension {
|
||||
|
||||
private RegisteredExtensionData<EntryExtensionsData, ReadWriteEntryDataExtension> readWriteEntryDataExtension;
|
||||
private DefaultMiddlewareContainer<TweedEntryReader<?, ?>> entryReaderMiddlewareContainer;
|
||||
private DefaultMiddlewareContainer<TweedEntryWriter<?, ?>> entryWriterMiddlewareContainer;
|
||||
private Map<Class<?>, RegisteredExtensionDataImpl<ReadWriteContextExtensionsData, ?>> readWriteContextExtensionsDataClasses;
|
||||
private PatchworkClass<ReadWriteContextExtensionsData> readWriteContextExtensionsDataPatchwork;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "read-write";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup(TweedExtensionSetupContext context) {
|
||||
readWriteEntryDataExtension = context.registerEntryExtensionData(ReadWriteEntryDataExtension.class);
|
||||
context.registerEntryExtensionData(EntryReaderWriterDefinition.class);
|
||||
|
||||
Collection<TweedExtension> extensions = context.configContainer().extensions();
|
||||
|
||||
readWriteContextExtensionsDataClasses = new HashMap<>(extensions.size());
|
||||
|
||||
ReadWriteExtensionSetupContext setupContext = new ReadWriteExtensionSetupContext() {
|
||||
@Override
|
||||
public <E> RegisteredExtensionData<ReadWriteContextExtensionsData, E> registerReadWriteContextExtensionData(Class<E> extensionDataClass) {
|
||||
if (readWriteContextExtensionsDataClasses.containsKey(extensionDataClass)) {
|
||||
throw new IllegalArgumentException("Context extension " + extensionDataClass.getName() + " is already registered");
|
||||
}
|
||||
RegisteredExtensionDataImpl<ReadWriteContextExtensionsData, E> registeredExtensionData = new RegisteredExtensionDataImpl<>();
|
||||
readWriteContextExtensionsDataClasses.put(extensionDataClass, registeredExtensionData);
|
||||
return registeredExtensionData;
|
||||
}
|
||||
};
|
||||
|
||||
entryReaderMiddlewareContainer = new DefaultMiddlewareContainer<>();
|
||||
entryWriterMiddlewareContainer = new DefaultMiddlewareContainer<>();
|
||||
|
||||
for (TweedExtension extension : extensions) {
|
||||
if (extension instanceof ReadWriteRelatedExtension) {
|
||||
ReadWriteRelatedExtension rwExtension = (ReadWriteRelatedExtension) extension;
|
||||
|
||||
rwExtension.setupReadWriteExtension(setupContext);
|
||||
|
||||
if (rwExtension.entryReaderMiddleware() != null) {
|
||||
entryReaderMiddlewareContainer.register(rwExtension.entryReaderMiddleware());
|
||||
}
|
||||
if (rwExtension.entryWriterMiddleware() != null) {
|
||||
entryWriterMiddlewareContainer.register(rwExtension.entryWriterMiddleware());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entryReaderMiddlewareContainer.seal();
|
||||
entryWriterMiddlewareContainer.seal();
|
||||
|
||||
PatchworkClassCreator<ReadWriteContextExtensionsData> patchworkClassCreator = PatchworkClassCreator.<ReadWriteContextExtensionsData>builder()
|
||||
.patchworkInterface(ReadWriteContextExtensionsData.class)
|
||||
.classPackage("de.siphalor.tweed5.data.extension.generated")
|
||||
.classPrefix("ReadWriteContextExtensionsData$")
|
||||
.build();
|
||||
|
||||
try {
|
||||
readWriteContextExtensionsDataPatchwork = patchworkClassCreator.createClass(readWriteContextExtensionsDataClasses.keySet());
|
||||
} catch (PatchworkClassGenerator.GenerationException e) {
|
||||
throw new IllegalStateException("Failed to generate read write context extensions' data patchwork class", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initEntry(ConfigEntry<?> configEntry) {
|
||||
TweedEntryReader<?, ?> baseReader;
|
||||
TweedEntryWriter<?, ?> baseWriter;
|
||||
if (configEntry.extensionsData().isPatchworkPartSet(EntryReaderWriterDefinition.class)) {
|
||||
EntryReaderWriterDefinition rwDefintion = (EntryReaderWriterDefinition) configEntry.extensionsData();
|
||||
baseReader = rwDefintion.reader();
|
||||
baseWriter = rwDefintion.writer();
|
||||
} else {
|
||||
baseReader = TweedEntryReaderWriterImpls.NOOP_READER_WRITER;
|
||||
baseWriter = TweedEntryReaderWriterImpls.NOOP_READER_WRITER;
|
||||
}
|
||||
|
||||
readWriteEntryDataExtension.set(configEntry.extensionsData(), new ReadWriteEntryDataExtensionImpl(
|
||||
entryReaderMiddlewareContainer.process(baseReader),
|
||||
entryWriterMiddlewareContainer.process(baseWriter)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadWriteContextExtensionsData createReadWriteContextExtensionsData() {
|
||||
try {
|
||||
return (ReadWriteContextExtensionsData) readWriteContextExtensionsDataPatchwork.constructor().invoke();
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException("Failed to instantiate read write context extensions' data", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T read(TweedDataReader reader, ConfigEntry<T> entry, ReadWriteContextExtensionsData contextExtensionsData) throws TweedEntryReadException {
|
||||
try {
|
||||
return getReaderChain(entry).read(reader, entry, new TweedReadWriteContextImpl(contextExtensionsData));
|
||||
} catch (TweedDataReadException e) {
|
||||
throw new TweedEntryReadException("Failed to read entry", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void write(TweedDataWriter writer, T value, ConfigEntry<T> entry, ReadWriteContextExtensionsData contextExtensionsData) throws TweedEntryWriteException {
|
||||
try {
|
||||
getWriterChain(entry).write(writer, value, entry, new TweedReadWriteContextImpl(contextExtensionsData));
|
||||
} catch (TweedDataWriteException e) {
|
||||
throw new TweedEntryWriteException("Failed to write entry", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Value
|
||||
private static class ReadWriteEntryDataExtensionImpl implements ReadWriteEntryDataExtension {
|
||||
TweedEntryReader<?, ?> entryReaderChain;
|
||||
TweedEntryWriter<?, ?> entryWriterChain;
|
||||
}
|
||||
|
||||
@Setter
|
||||
private static class RegisteredExtensionDataImpl<U extends Patchwork<U>, E> implements RegisteredExtensionData<U, E> {
|
||||
private MethodHandle setter;
|
||||
|
||||
@Override
|
||||
public void set(U patchwork, E extension) {
|
||||
try {
|
||||
setter.invokeWithArguments(patchwork, extension);
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static <T> TweedEntryReader<T, ConfigEntry<T>> getReaderChain(ConfigEntry<T> elementEntry) {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryReader<T, ConfigEntry<T>>) ((ReadWriteEntryDataExtension) elementEntry.extensionsData()).entryReaderChain();
|
||||
}
|
||||
|
||||
static <T> TweedEntryWriter<T, ConfigEntry<T>> getWriterChain(ConfigEntry<T> elementEntry) {
|
||||
//noinspection unchecked
|
||||
return (TweedEntryWriter<T, ConfigEntry<T>>) ((ReadWriteEntryDataExtension) elementEntry.extensionsData()).entryWriterChain();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.entry.CoherentCollectionConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||
import de.siphalor.tweed5.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 java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
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<Collection<Object>, CoherentCollectionConfigEntry<Object, Collection<Object>>> COHERENT_COLLECTION_READER_WRITER = new CoherentCollectionReaderWriter<>();
|
||||
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
|
||||
private static class PrimitiveReaderWriter<T> implements TweedEntryReaderWriter<T, ConfigEntry<T>> {
|
||||
private final Function<TweedDataToken, T> readerCall;
|
||||
private final BiConsumer<TweedDataWriter, T> writerCall;
|
||||
|
||||
@Override
|
||||
public T read(TweedDataReader reader, ConfigEntry<T> entry, TweedReadContext context) throws TweedDataReadException {
|
||||
return readerCall.apply(reader.readToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataWriter writer, T value, ConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||
requireNonNullWriteValue(value);
|
||||
writerCall.accept(writer, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CoherentCollectionReaderWriter<T, C extends Collection<T>> implements TweedEntryReaderWriter<C, CoherentCollectionConfigEntry<T, C>> {
|
||||
@Override
|
||||
public C read(TweedDataReader reader, CoherentCollectionConfigEntry<T, C> entry, TweedReadContext context) throws TweedEntryReadException, TweedDataReadException {
|
||||
assertIsToken(reader.readToken(), TweedDataToken::isListStart, "Expected list start");
|
||||
TweedDataToken token = reader.peekToken();
|
||||
if (token.isListEnd()) {
|
||||
return entry.instantiateCollection(0);
|
||||
}
|
||||
|
||||
ConfigEntry<T> elementEntry = entry.elementEntry();
|
||||
TweedEntryReader<T, ConfigEntry<T>> elementReader = ReadWriteExtensionImpl.getReaderChain(elementEntry);
|
||||
|
||||
List<T> list = new ArrayList<>(20);
|
||||
while (true) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
C result = entry.instantiateCollection(list.size());
|
||||
result.addAll(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataWriter writer, C value, CoherentCollectionConfigEntry<T, C> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||
requireNonNullWriteValue(value);
|
||||
|
||||
if (value.isEmpty()) {
|
||||
writer.visitEmptyList();
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigEntry<T> elementEntry = entry.elementEntry();
|
||||
TweedEntryWriter<T, ConfigEntry<T>> elementWriter = ReadWriteExtensionImpl.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, TweedDataReadException {
|
||||
assertIsToken(reader.readToken(), TweedDataToken::isMapStart, "Expected map start");
|
||||
|
||||
Map<String, ConfigEntry<?>> compoundEntries = entry.subEntries();
|
||||
T compoundValue = entry.instantiateCompoundValue();
|
||||
while (true) {
|
||||
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);
|
||||
TweedEntryReader<Object, ConfigEntry<Object>> subEntryReaderChain = ReadWriteExtensionImpl.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");
|
||||
}
|
||||
}
|
||||
return compoundValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataWriter writer, T value, CompoundConfigEntry<T> entry, TweedWriteContext context) throws TweedEntryWriteException, TweedDataWriteException {
|
||||
requireNonNullWriteValue(value);
|
||||
|
||||
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();
|
||||
|
||||
writer.visitMapEntryKey(key);
|
||||
|
||||
TweedEntryWriter<Object, ConfigEntry<Object>> subEntryWriterChain = ReadWriteExtensionImpl.getWriterChain(subEntry);
|
||||
subEntryWriterChain.write(writer, entry.get(value, key), subEntry, context);
|
||||
}
|
||||
|
||||
writer.visitMapEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NoopReaderWriter implements TweedEntryReaderWriter<Object, ConfigEntry<Object>> {
|
||||
@Override
|
||||
public Object read(TweedDataReader reader, ConfigEntry<Object> entry, TweedReadContext context) throws TweedDataReadException {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(TweedDataWriter writer, Object value, ConfigEntry<Object> entry, TweedWriteContext context) throws TweedDataWriteException {
|
||||
writer.visitNull();
|
||||
}
|
||||
|
||||
private enum Context {
|
||||
LIST, MAP,
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void requireNonNullWriteValue(T value) throws TweedEntryWriteException {
|
||||
if (value == null) {
|
||||
throw new TweedEntryWriteException("Unable to write null value");
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertIsToken(TweedDataToken token, Predicate<TweedDataToken> isToken, String description) throws TweedEntryReadException {
|
||||
if (!isToken.test(token)) {
|
||||
throw new TweedEntryReadException("Unexpected token " + token + ": " + description);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import de.siphalor.tweed5.data.extension.api.TweedReadContext;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedWriteContext;
|
||||
import de.siphalor.tweed5.data.extension.api.extension.ReadWriteContextExtensionsData;
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class TweedReadWriteContextImpl implements TweedReadContext, TweedWriteContext {
|
||||
ReadWriteContextExtensionsData extensionsData;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@ApiStatus.Internal
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -0,0 +1,118 @@
|
||||
package de.siphalor.tweed5.data.extension.impl;
|
||||
|
||||
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||
import de.siphalor.tweed5.core.api.extension.EntryExtensionsData;
|
||||
import de.siphalor.tweed5.core.api.extension.RegisteredExtensionData;
|
||||
import de.siphalor.tweed5.core.impl.DefaultConfigContainer;
|
||||
import de.siphalor.tweed5.core.impl.entry.CoherentCollectionConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.SimpleConfigEntryImpl;
|
||||
import de.siphalor.tweed5.core.impl.entry.StaticMapCompoundConfigEntryImpl;
|
||||
import de.siphalor.tweed5.data.extension.api.EntryReaderWriterDefinition;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryReader;
|
||||
import de.siphalor.tweed5.data.extension.api.TweedEntryWriter;
|
||||
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.TweedEntryReaderWriters;
|
||||
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.TweedDataWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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 org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class ReadWriteExtensionImplTest {
|
||||
private StringWriter stringWriter;
|
||||
private StaticMapCompoundConfigEntryImpl<Map<String, Object>> rootEntry;
|
||||
private ReadWriteExtension readWriteExtension;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ConfigContainer<Map<String, Object>> configContainer = new DefaultConfigContainer<>();
|
||||
|
||||
readWriteExtension = new ReadWriteExtensionImpl();
|
||||
configContainer.registerExtension(readWriteExtension);
|
||||
configContainer.finishExtensionSetup();
|
||||
|
||||
rootEntry = new StaticMapCompoundConfigEntryImpl<>(((Class<Map<String, Object>>) (Class<?>) Map.class), LinkedHashMap::new);
|
||||
|
||||
SimpleConfigEntryImpl<Integer> intEntry = new SimpleConfigEntryImpl<>(Integer.class);
|
||||
rootEntry.addSubEntry("int", intEntry);
|
||||
|
||||
CoherentCollectionConfigEntryImpl<Boolean, List<Boolean>> listEntry = new CoherentCollectionConfigEntryImpl<>((Class<List<Boolean>>) (Class<?>) List.class, ArrayList::new);
|
||||
rootEntry.addSubEntry("list", listEntry);
|
||||
|
||||
SimpleConfigEntryImpl<Boolean> booleanEntry = new SimpleConfigEntryImpl<>(Boolean.class);
|
||||
listEntry.elementEntry(booleanEntry);
|
||||
|
||||
configContainer.attachAndSealTree(rootEntry);
|
||||
|
||||
RegisteredExtensionData<EntryExtensionsData, EntryReaderWriterDefinition> readerWriterData = (RegisteredExtensionData<EntryExtensionsData, EntryReaderWriterDefinition>) configContainer.entryDataExtensions().get(EntryReaderWriterDefinition.class);
|
||||
readerWriterData.set(rootEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.compoundReaderWriter()));
|
||||
readerWriterData.set(intEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.intReaderWriter()));
|
||||
readerWriterData.set(listEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.coherentCollectionReaderWriter()));
|
||||
readerWriterData.set(booleanEntry.extensionsData(), new TrivialEntryReaderWriterDefinition(TweedEntryReaderWriters.booleanReaderWriter()));
|
||||
|
||||
configContainer.initialize();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void write() {
|
||||
Map<String, Object> value = new HashMap<>();
|
||||
value.put("int", 123);
|
||||
value.put("list", Arrays.asList(true, false, true));
|
||||
|
||||
assertDoesNotThrow(() -> readWriteExtension.write(
|
||||
setupWriter(writer -> new HjsonWriter(writer, new HjsonWriter.Options())),
|
||||
value,
|
||||
rootEntry,
|
||||
readWriteExtension.createReadWriteContextExtensionsData()
|
||||
));
|
||||
|
||||
assertEquals("{\n\tint: 123\n\tlist: [\n\t\ttrue\n\t\tfalse\n\t\ttrue\n\t]\n}\n", stringWriter.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void read() {
|
||||
Map<String, Object> result = assertDoesNotThrow(() -> readWriteExtension.read(
|
||||
new HjsonReader(new HjsonLexer(new StringReader("{\n\tint: 123\n\tlist: [\n\t\ttrue\n\t\tfalse\n\t\ttrue\n\t]\n}\n"))),
|
||||
rootEntry,
|
||||
readWriteExtension.createReadWriteContextExtensionsData()
|
||||
));
|
||||
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(123, result.get("int"));
|
||||
assertEquals(Arrays.asList(true, false, true), result.get("list"));
|
||||
}
|
||||
|
||||
private TweedDataWriter setupWriter(Function<Writer, TweedDataWriter> writerFactory) {
|
||||
stringWriter = new StringWriter();
|
||||
return writerFactory.apply(stringWriter);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class TrivialEntryReaderWriterDefinition implements EntryReaderWriterDefinition {
|
||||
private final TweedEntryReaderWriter<?, ?> readerWriter;
|
||||
|
||||
@Override
|
||||
public TweedEntryReader<?, ?> reader() {
|
||||
return readerWriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TweedEntryWriter<?, ?> writer() {
|
||||
return readerWriter;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user