Initial commit

That's a lotta stuff for an initial commit, but well...
This commit is contained in:
2024-05-25 19:22:26 +02:00
commit b0f35b03b9
99 changed files with 6476 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
package de.siphalor.tweed5.data.extension.api;
public interface EntryReaderWriterDefinition {
TweedEntryReader<?, ?> reader();
TweedEntryWriter<?, ?> writer();
}

View File

@@ -0,0 +1,6 @@
package de.siphalor.tweed5.data.extension.api;
public interface ReadWriteEntryDataExtension {
TweedEntryReader<?, ?> entryReaderChain();
TweedEntryWriter<?, ?> entryWriterChain();
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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> {
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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> {}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,4 @@
@ApiStatus.Internal
package de.siphalor.tweed5.data.extension.impl;
import org.jetbrains.annotations.ApiStatus;

View File

@@ -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;
}
}
}