Rework mutability of nodes

This commit is contained in:
Christopher Schnick 2021-12-18 03:19:18 +01:00
parent cf89b2ff18
commit 562f02b607
25 changed files with 503 additions and 208 deletions

View file

@ -99,7 +99,7 @@ public class DataTableImpl implements DataTable {
}, false); }, false);
} }
}.execute(); }.execute();
return ArrayNode.wrap(nodes); return ArrayNode.of(nodes);
} }
@Override @Override

View file

@ -20,6 +20,8 @@ dependencies {
compileOnly 'org.projectlombok:lombok:1.18.22' compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22' annotationProcessor 'org.projectlombok:lombok:1.18.22'
testCompileOnly 'org.projectlombok:lombok:1.18.22'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'

View file

@ -1,5 +1,8 @@
package io.xpipe.core.data; package io.xpipe.core.data;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.type.DataType; import io.xpipe.core.data.type.DataType;
import java.util.Iterator; import java.util.Iterator;
@ -29,6 +32,10 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
throw unsupported("set raw data"); throw unsupported("set raw data");
} }
public DataStructureNode setNull() {
throw unsupported("set null");
}
public DataStructureNode set(int index, DataStructureNode node) { public DataStructureNode set(int index, DataStructureNode node) {
throw unsupported("set at index"); throw unsupported("set at index");
} }
@ -47,6 +54,34 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
return false; return false;
} }
public boolean isNull() {
throw unsupported("null check");
}
public final ValueNode asValue() {
if (!isValue()) {
throw new UnsupportedOperationException(getName() + " is not a value node");
}
return (ValueNode) this;
}
public final TupleNode asTuple() {
if (!isTuple()) {
throw new UnsupportedOperationException(getName() + " is not a tuple node");
}
return (TupleNode) this;
}
public final ArrayNode asArray() {
if (!isArray()) {
throw new UnsupportedOperationException(getName() + " is not an array node");
}
return (ArrayNode) this;
}
public DataStructureNode put(String keyName, DataStructureNode node) { public DataStructureNode put(String keyName, DataStructureNode node) {
throw unsupported("put node with key"); throw unsupported("put node with key");
} }
@ -67,7 +102,7 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
throw unsupported("size computation"); throw unsupported("size computation");
} }
public abstract DataType getDataType(); public abstract DataType determineDataType();
public DataStructureNode at(int index) { public DataStructureNode at(int index) {
throw unsupported("integer indexing"); throw unsupported("integer indexing");

View file

@ -92,7 +92,7 @@ public class GenericArrayReader implements GenericAbstractReader {
throw new IllegalStateException("Array ended but is not full yet"); throw new IllegalStateException("Array ended but is not full yet");
} }
created = ArrayNode.wrap(nodes); created = ArrayNode.of(nodes);
} }
@Override @Override
@ -142,7 +142,7 @@ public class GenericArrayReader implements GenericAbstractReader {
throw new IllegalStateException("Array is full but got another value"); throw new IllegalStateException("Array is full but got another value");
} }
put(ValueNode.wrap(value)); put(ValueNode.mutable(value));
} }
@Override @Override
@ -156,6 +156,6 @@ public class GenericArrayReader implements GenericAbstractReader {
throw new IllegalStateException(); throw new IllegalStateException();
} }
return ArrayNode.wrap(nodes); return ArrayNode.of(nodes);
} }
} }

View file

@ -84,6 +84,6 @@ public class GenericDataStructureNodeReader implements GenericDataStreamCallback
return; return;
} }
node = ValueNode.wrap(value); node = ValueNode.mutable(value);
} }
} }

View file

@ -149,7 +149,7 @@ public class GenericTupleReader implements GenericAbstractReader {
throw new IllegalStateException("Tuple is full but got another value"); throw new IllegalStateException("Tuple is full but got another value");
} }
putNode(ValueNode.wrap(value)); putNode(ValueNode.mutable(value));
} }
@Override @Override

View file

@ -19,14 +19,14 @@ public class ArrayNode extends DataStructureNode {
} }
public static ArrayNode of(DataStructureNode... dsn) { public static ArrayNode of(DataStructureNode... dsn) {
return wrap(List.of(dsn)); return of(List.of(dsn));
} }
public static ArrayNode wrap(List<DataStructureNode> valueNodes) { public static ArrayNode of(List<DataStructureNode> valueNodes) {
return new ArrayNode(valueNodes); return new ArrayNode(valueNodes);
} }
public static ArrayNode copy(List<DataStructureNode> valueNodes) { public static ArrayNode copyOf(List<DataStructureNode> valueNodes) {
return new ArrayNode(new ArrayList<>(valueNodes)); return new ArrayNode(new ArrayList<>(valueNodes));
} }
@ -69,8 +69,8 @@ public class ArrayNode extends DataStructureNode {
} }
@Override @Override
public ArrayType getDataType() { public ArrayType determineDataType() {
return ArrayType.of(valueNodes.stream().map(DataStructureNode::getDataType).toList()); return ArrayType.of(valueNodes.stream().map(DataStructureNode::determineDataType).toList());
} }
@Override @Override

View file

@ -0,0 +1,26 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
public class ImmutableValueNode extends ValueNode {
private final byte[] data;
ImmutableValueNode(byte[] data) {
this.data = data;
}
@Override
public String toString(int indent) {
return getClass().getSimpleName() + "(" + new String(data) + ")";
}
@Override
public DataStructureNode setRawData(byte[] data) {
throw new UnsupportedOperationException("Value node is immutable");
}
public byte[] getRawData() {
return data;
}
}

View file

@ -0,0 +1,27 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
public class MutableValueNode extends ValueNode {
private byte[] data;
MutableValueNode(byte[] data) {
this.data = data;
}
@Override
public String toString(int indent) {
return getClass().getSimpleName() + "(" + new String(data) + ")";
}
@Override
public DataStructureNode setRawData(byte[] data) {
this.data = data;
return this;
}
public byte[] getRawData() {
return data;
}
}

View file

@ -17,8 +17,14 @@ public class NoKeyTupleNode extends TupleNode {
} }
@Override @Override
public DataType getDataType() { public DataStructureNode set(int index, DataStructureNode node) {
return TupleType.wrap(null, nodes.stream().map(DataStructureNode::getDataType).toList()); nodes.set(index, node);
return this;
}
@Override
public DataType determineDataType() {
return TupleType.of(null, nodes.stream().map(DataStructureNode::determineDataType).toList());
} }
@Override @Override

View file

@ -19,8 +19,14 @@ public class SimpleTupleNode extends TupleNode {
} }
@Override @Override
public DataType getDataType() { public DataStructureNode set(int index, DataStructureNode node) {
return TupleType.wrap(names, nodes.stream().map(DataStructureNode::getDataType).toList()); nodes.set(index, node);
return this;
}
@Override
public DataType determineDataType() {
return TupleType.of(names, nodes.stream().map(DataStructureNode::determineDataType).toList());
} }
@Override @Override
@ -50,6 +56,7 @@ public class SimpleTupleNode extends TupleNode {
@Override @Override
public DataStructureNode clear() { public DataStructureNode clear() {
nodes.clear(); nodes.clear();
names.clear();
return this; return this;
} }

View file

@ -8,59 +8,68 @@ import lombok.EqualsAndHashCode;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@EqualsAndHashCode(callSuper = false) @EqualsAndHashCode(callSuper = false)
public class ValueNode extends DataStructureNode { public abstract class ValueNode extends DataStructureNode {
private byte[] data; private static final byte[] NULL = new byte[] {0};
private ValueNode(byte[] data) { public static ValueNode immutable(byte[] data) {
this.data = data; return new ImmutableValueNode(data);
} }
public static ValueNode wrap(byte[] data) { public static ValueNode mutableNull() {
return new ValueNode(data); return mutable(NULL);
}
public static ValueNode nullValue() {
return mutable(NULL);
}
public static ValueNode mutable(byte[] data) {
return new MutableValueNode(data);
}
public static ValueNode mutable(Object o) {
return mutable(o.toString().getBytes(StandardCharsets.UTF_8));
}
public static ValueNode of(byte[] data) {
return mutable(data);
} }
public static ValueNode of(Object o) { public static ValueNode of(Object o) {
return new ValueNode(o.toString().getBytes(StandardCharsets.UTF_8)); return mutable(o);
}
protected ValueNode() {
} }
@Override @Override
public DataStructureNode setRawData(byte[] data) { public abstract DataStructureNode setRawData(byte[] data);
this.data = data;
return this;
}
@Override @Override
public boolean isValue() { public final int asInt() {
return true;
}
@Override
public int asInt() {
return Integer.parseInt(asString()); return Integer.parseInt(asString());
} }
@Override @Override
public String asString() { public final String asString() {
return new String(data); return new String(getRawData());
} }
@Override @Override
protected String getName() { public final boolean isValue() {
return true;
}
@Override
protected final String getName() {
return "value node"; return "value node";
} }
@Override @Override
public String toString(int indent) { public final DataType determineDataType() {
return new String(data); return ValueType.of();
} }
@Override public abstract byte[] getRawData();
public DataType getDataType() {
return new ValueType();
}
public byte[] getRawData() {
return data;
}
} }

View file

@ -11,14 +11,18 @@ import java.util.List;
@EqualsAndHashCode @EqualsAndHashCode
public class ArrayType implements DataType { public class ArrayType implements DataType {
public static ArrayType ofWildcard() {
return new ArrayType(WildcardType.of());
}
public static ArrayType of(List<DataType> types) { public static ArrayType of(List<DataType> types) {
if (types.size() == 0) { if (types.size() == 0) {
return new ArrayType(new WildcardType()); return new ArrayType(WildcardType.of());
} }
var first = types.get(0); var first = types.get(0);
var eq = types.stream().allMatch(d -> d.equals(first)); var eq = types.stream().allMatch(d -> d.equals(first));
return new ArrayType(eq ? first : new WildcardType()); return new ArrayType(eq ? first : WildcardType.of());
} }
private final DataType sharedType; private final DataType sharedType;

View file

@ -26,7 +26,7 @@ public class TupleType implements DataType {
return new TupleType(List.of(), List.of()); return new TupleType(List.of(), List.of());
} }
public static TupleType wrap(List<String> names, List<DataType> types) { public static TupleType of(List<String> names, List<DataType> types) {
return new TupleType(names, types); return new TupleType(names, types);
} }

View file

@ -9,6 +9,14 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode @EqualsAndHashCode
public class ValueType implements DataType { public class ValueType implements DataType {
public static ValueType of() {
return new ValueType();
}
private ValueType() {
}
@Override @Override
public String getName() { public String getName() {
return "value"; return "value";

View file

@ -5,6 +5,14 @@ import io.xpipe.core.data.type.callback.DataTypeCallback;
public class WildcardType implements DataType { public class WildcardType implements DataType {
public static WildcardType of() {
return new WildcardType();
}
private WildcardType() {
}
@Override @Override
public String getName() { public String getName() {
return "wildcard"; return "wildcard";

View file

@ -13,7 +13,7 @@ public interface DataTypeCallback {
return new DataTypeCallback() { return new DataTypeCallback() {
@Override @Override
public void onValue() { public void onValue() {
typeConsumer.accept(new ValueType()); typeConsumer.accept(ValueType.of());
} }
@Override @Override

View file

@ -50,7 +50,7 @@ public class TypedDataStreamWriter {
if (!type.getTypes().get(i).isWildcard()) { if (!type.getTypes().get(i).isWildcard()) {
write(out, tuple.at(i), type.getTypes().get(i)); write(out, tuple.at(i), type.getTypes().get(i));
} else { } else {
GenericDataStreamWriter.write(out, tuple); GenericDataStreamWriter.write(out, tuple.at(i));
} }
} }
} }

View file

@ -11,25 +11,35 @@ import io.xpipe.core.data.type.callback.DataTypeCallback;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
public class TypedDataStructureNodeReader implements TypedAbstractReader { public class TypedDataStructureNodeReader implements TypedAbstractReader {
public static TypedDataStructureNodeReader mutable(DataType type) {
return new TypedDataStructureNodeReader(type, false);
}
public static TypedDataStructureNodeReader immutable(DataType type) {
return new TypedDataStructureNodeReader(type, true);
}
private int currentDataTypeIndex; private int currentDataTypeIndex;
private final List<DataType> flattened; private final List<DataType> flattened;
private Stack<List<DataStructureNode>> children; private final Stack<List<DataStructureNode>> children;
private Stack<DataStructureNode> nodes; private final Stack<DataStructureNode> nodes;
private DataStructureNode readNode; private DataStructureNode readNode;
private boolean initialized; private boolean initialized;
private int arrayDepth; private int arrayDepth;
private boolean makeImmutable; private final boolean makeImmutable;
public TypedDataStructureNodeReader(DataType type) { private TypedDataStructureNodeReader(DataType type, boolean makeImmutable) {
flattened = new ArrayList<>(); flattened = new ArrayList<>();
children = new Stack<>(); children = new Stack<>();
nodes = new Stack<>(); nodes = new Stack<>();
type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d))); type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d)));
this.makeImmutable = makeImmutable;
} }
@Override @Override
@ -61,55 +71,22 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
@Override @Override
public void onValue(byte[] data) { public void onValue(byte[] data) {
var val = makeImmutable ? ValueNode.immutable(data) : ValueNode.mutable(data);
if (!initialized) { if (!initialized) {
readNode = ValueNode.wrap(data); readNode = val;
return; return;
} }
children.peek().add(ValueNode.wrap(data)); children.peek().add(val);
if (!flattened.get(currentDataTypeIndex).isArray()) { if (!flattened.get(currentDataTypeIndex).isArray()) {
currentDataTypeIndex++; currentDataTypeIndex++;
} }
} }
private void finishTuple() {
children.pop();
var popped = nodes.pop();
if (!popped.isTuple()) {
throw new IllegalStateException();
}
SimpleTupleNode tuple = (SimpleTupleNode) popped;
if (tuple.getNames().size() != tuple.getNodes().size()) {
throw new IllegalStateException("");
}
if (nodes.empty()) {
readNode = popped;
} else {
children.peek().add(popped);
}
}
private boolean isInArray() { private boolean isInArray() {
return arrayDepth >= 1; return arrayDepth >= 1;
} }
private void finishArray() {
arrayDepth--;
if (!isInArray()) {
currentDataTypeIndex++;
}
children.pop();
var popped = nodes.pop();
if (nodes.empty()) {
readNode = popped;
} else {
children.peek().add(popped);
}
}
@Override @Override
public void onGenericNode(DataStructureNode node) { public void onGenericNode(DataStructureNode node) {
children.peek().add(node); children.peek().add(node);
@ -134,13 +111,32 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
var l = new ArrayList<DataStructureNode>(tupleType.getSize()); var l = new ArrayList<DataStructureNode>(tupleType.getSize());
children.push(l); children.push(l);
var newNode = TupleNode.wrapRaw(tupleType.getNames(), l);
var tupleNames = makeImmutable ?
Collections.unmodifiableList(tupleType.getNames()) : new ArrayList<>(tupleType.getNames());
var tupleNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
var newNode = TupleNode.wrapRaw(tupleNames, tupleNodes);
nodes.push(newNode); nodes.push(newNode);
} }
@Override @Override
public void onTupleEnd() { public void onTupleEnd() {
finishTuple(); children.pop();
var popped = nodes.pop();
if (!popped.isTuple()) {
throw new IllegalStateException();
}
SimpleTupleNode tuple = (SimpleTupleNode) popped;
if (tuple.getNames().size() != tuple.getNodes().size()) {
throw new IllegalStateException("");
}
if (nodes.empty()) {
readNode = popped;
} else {
children.peek().add(popped);
}
} }
@Override @Override
@ -151,13 +147,26 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
var l = new ArrayList<DataStructureNode>(); var l = new ArrayList<DataStructureNode>();
children.push(l); children.push(l);
var newNode = ArrayNode.wrap(l);
var arrayNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
var newNode = ArrayNode.of(arrayNodes);
nodes.push(newNode); nodes.push(newNode);
arrayDepth++; arrayDepth++;
} }
@Override @Override
public void onArrayEnd() { public void onArrayEnd() {
finishArray(); arrayDepth--;
if (!isInArray()) {
currentDataTypeIndex++;
}
children.pop();
var popped = nodes.pop();
if (nodes.empty()) {
readNode = popped;
} else {
children.peek().add(popped);
}
} }
} }

View file

@ -11,7 +11,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
public class TypedReusableDataStructureNodeReader implements TypedDataStreamCallback { public class TypedReusableDataStructureNodeReader implements TypedAbstractReader {
private TypedDataStructureNodeReader initialReader; private TypedDataStructureNodeReader initialReader;
private DataStructureNode node; private DataStructureNode node;
@ -22,10 +22,15 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
public TypedReusableDataStructureNodeReader(DataType type) { public TypedReusableDataStructureNodeReader(DataType type) {
flattened = new ArrayList<>(); flattened = new ArrayList<>();
indices = new Stack<>(); indices = new Stack<>();
initialReader = new TypedDataStructureNodeReader(type); initialReader = TypedDataStructureNodeReader.mutable(type);
type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d))); type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d)));
} }
@Override
public boolean isDone() {
return true;
}
public DataStructureNode create() { public DataStructureNode create() {
return node; return node;
} }
@ -47,10 +52,12 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
} }
if (isInArray()) { if (isInArray()) {
getCurrentParent().set(indices.peek(), ValueNode.wrap(data)); getCurrentParent().set(indices.peek(), ValueNode.mutable(data));
indices.push(indices.pop() + 1);
} else { } else {
getCurrent().setRawData(data); getCurrent().setRawData(data);
}
if (!indices.isEmpty()) {
indices.push(indices.pop() + 1); indices.push(indices.pop() + 1);
} }
} }
@ -62,11 +69,8 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
return; return;
} }
if (isInArray()) { getCurrentParent().set(indices.peek(), node);
getCurrentParent().set(indices.peek(), node); if (!indices.isEmpty()) {
indices.push(indices.pop() + 1);
} else {
getCurrent().set(indices.peek(), node);
indices.push(indices.pop() + 1); indices.push(indices.pop() + 1);
} }
} }
@ -140,7 +144,9 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
indices.pop(); indices.pop();
arrayDepth--; arrayDepth--;
indices.push(indices.pop() + 1); if (!indices.isEmpty()) {
indices.push(indices.pop() + 1);
}
} }
@Override @Override

View file

@ -1,22 +1,34 @@
package io.xpipe.core.test; package io.xpipe.core.test;
import io.xpipe.core.data.DataStructureNode; import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.generic.GenericDataStreamParser;
import io.xpipe.core.data.generic.GenericDataStreamWriter;
import io.xpipe.core.data.generic.GenericDataStructureNodeReader;
import io.xpipe.core.data.node.ArrayNode; import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode; import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode; import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.typed.TypedDataStreamParser;
import io.xpipe.core.data.typed.TypedDataStreamWriter;
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
public class DataStructureTest { public class DataStructureTest {
public static DataStructureNode createTestData() { public static DataStructureNode createTestData() {
var val = ValueNode.wrap("value".getBytes(StandardCharsets.UTF_8)); var val = ValueNode.of("value");
var flatArray = ArrayNode.wrap(List.of(ValueNode.of(1), ValueNode.of(2))); var flatArray = ArrayNode.of(List.of(ValueNode.of(1), ValueNode.of(2)));
var flatTuple = TupleNode.builder().add("key1", val).build(); var flatTuple = TupleNode.builder().add("key1", val).build();
var nestedArray = ArrayNode.wrap(List.of(flatArray, flatTuple)); var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
return TupleNode.builder() return TupleNode.builder()
.add("key1", val) .add("key1", val)
.add("key2", flatArray) .add("key2", flatArray)
@ -53,4 +65,100 @@ public class DataStructureTest {
Assertions.assertEquals(key4.at(0), ArrayNode.of(ValueNode.of(1), ValueNode.of(2))); Assertions.assertEquals(key4.at(0), ArrayNode.of(ValueNode.of(1), ValueNode.of(2)));
Assertions.assertEquals(key4.at(0).at(0).asInt(), 1); Assertions.assertEquals(key4.at(0).at(0).asInt(), 1);
} }
@ParameterizedTest
@EnumSource(DataStructureTests.TypedDataset.class)
public void testTypes(DataStructureTests.TypedDataset ds) throws IOException {
for (var el : ds.nodes) {
Assertions.assertTrue(ds.type.matches(el));
}
}
@ParameterizedTest
@EnumSource(DataStructureTests.TypedDataset.class)
public void testGenericIo(DataStructureTests.TypedDataset ds) throws IOException {
for (var el : ds.nodes) {
var dataOut = new ByteArrayOutputStream();
GenericDataStreamWriter.write(dataOut, el);
var data = dataOut.toByteArray();
var reader = new GenericDataStructureNodeReader();
GenericDataStreamParser.read(new ByteArrayInputStream(data), reader);
var readNode = reader.create();
Assertions.assertEquals(el, readNode);
}
}
@ParameterizedTest
@EnumSource(DataStructureTests.TypedDataset.class)
public void testMutableTypedIo(DataStructureTests.TypedDataset ds) throws IOException {
for (var node : ds.nodes) {
var dataOut = new ByteArrayOutputStream();
TypedDataStreamWriter.writeStructure(dataOut, node, ds.type);
var data = dataOut.toByteArray();
var reader = TypedDataStructureNodeReader.mutable(ds.type);
new TypedDataStreamParser(ds.type).readStructure(new ByteArrayInputStream(data), reader);
var readNode = reader.create();
Assertions.assertEquals(node, readNode);
Assertions.assertDoesNotThrow(() -> {
if (readNode.isTuple()) {
readNode.clear();
Assertions.assertEquals(readNode.size(), 0);
}
if (readNode.isArray()) {
readNode.clear();
Assertions.assertEquals(readNode.size(), 0);
}
});
}
}
@ParameterizedTest
@EnumSource(DataStructureTests.TypedDataset.class)
public void testImmutableTypedIo(DataStructureTests.TypedDataset ds) throws IOException {
for (var node : ds.nodes) {
var dataOut = new ByteArrayOutputStream();
TypedDataStreamWriter.writeStructure(dataOut, node, ds.type);
var data = dataOut.toByteArray();
var reader = TypedDataStructureNodeReader.immutable(ds.type);
new TypedDataStreamParser(ds.type).readStructure(new ByteArrayInputStream(data), reader);
var readNode = reader.create();
Assertions.assertEquals(node, readNode);
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
if (readNode.isTuple() || readNode.isArray()) {
readNode.clear();
Assertions.assertEquals(readNode.size(), 0);
} else {
readNode.setRawData("abc".getBytes(StandardCharsets.UTF_8));
}
});
if (readNode.isTuple() || readNode.isArray()) {
Assertions.assertEquals(readNode.size(), node.size());
}
}
}
@ParameterizedTest
@EnumSource(DataStructureTests.TypedDataset.class)
public void testReusableTypedIo(DataStructureTests.TypedDataset ds) throws IOException {
var dataOut = new ByteArrayOutputStream();
for (var node : ds.nodes) {
TypedDataStreamWriter.writeStructure(dataOut, node, ds.type);
}
var data = dataOut.toByteArray();
var in = new ByteArrayInputStream(data);
var reader = new TypedReusableDataStructureNodeReader(ds.type);
for (var node : ds.nodes) {
new TypedDataStreamParser(ds.type).readStructure(in, reader);
var readNode = reader.create();
Assertions.assertEquals(node, readNode);
}
}
} }

View file

@ -4,17 +4,47 @@ import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode; import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode; import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode; import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.type.*;
import lombok.AllArgsConstructor;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
public class DataStructureTests { public class DataStructureTests {
public static DataStructureNode createTestData() { @AllArgsConstructor
var val = ValueNode.wrap("value".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.wrap(List.of(ValueNode.of(1), ValueNode.of(2))); public static enum TypedDataset {
// Variety
DATA_1(createTestDataType1(), List.of(createTestData11(), createTestData12())),
// Multiple nested arrays
DATA_2(createTestDataType2(), List.of(createTestData21(), createTestData22())),
// Array with wildcard type
DATA_3(createTestData31().determineDataType(), List.of(createTestData31(), createTestData32())),
// Simple values
DATA_4(ValueType.of(), List.of(createTestData41(), createTestData42())),
// Array with wildcard type
DATA_5(createTestDataType5(), List.of(createTestData51(), createTestData52(), createTestData53())),
// Tuple with wildcard type
DATA_6(createTestDataType6(), List.of(createTestData61(), createTestData62()));
public DataType type;
public List<DataStructureNode> nodes;
}
private static DataStructureNode createTestData11() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.of(List.of(ValueNode.of(1), ValueNode.of(2)));
var flatTuple = TupleNode.builder().add("key1", val).build(); var flatTuple = TupleNode.builder().add("key1", val).build();
var nestedArray = ArrayNode.wrap(List.of(flatArray, flatTuple)); var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
return TupleNode.builder() return TupleNode.builder()
.add("key1", val) .add("key1", val)
.add("key2", flatArray) .add("key2", flatArray)
@ -22,4 +52,109 @@ public class DataStructureTests {
.add("key4", nestedArray) .add("key4", nestedArray)
.build(); .build();
} }
private static DataStructureNode createTestData12() {
var val = ValueNode.nullValue();
var flatArray = ArrayNode.of();
var flatTuple = TupleNode.builder().add("key1", val).build();
var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
return TupleNode.builder()
.add("key1", val)
.add("key2", flatArray)
.add("key3", flatTuple)
.add("key4", nestedArray)
.build();
}
private static DataType createTestDataType1() {
return createTestData11().determineDataType();
}
public static DataStructureNode createTestData21() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.of(List.of(ValueNode.of(1), ValueNode.of(2)));
var flatTuple = TupleNode.builder().add("key1", val).build();
var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
var doubleNested = ArrayNode.of(val, flatArray, flatTuple, nestedArray);
return doubleNested;
}
public static DataStructureNode createTestData22() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
return ArrayNode.of(val);
}
public static DataType createTestDataType2() {
return ArrayType.ofWildcard();
}
public static DataStructureNode createTestData31() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var flatTuple = TupleNode.builder().add("key1", val).build();
var flatArray = ArrayNode.of(List.of(val, flatTuple));
return flatArray;
}
public static DataStructureNode createTestData32() {
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
var flatTuple = TupleNode.builder().add("key1", ValueNode.nullValue()).add("key2", ValueNode.nullValue()).build();
var flatArray = ArrayNode.of(List.of(val, flatTuple));
return flatArray;
}
public static DataStructureNode createTestData41() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
return val;
}
public static DataStructureNode createTestData42() {
var val = ValueNode.nullValue();
return val;
}
public static DataStructureNode createTestData51() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.of(List.of(val, ValueNode.nullValue()));
var array1 = ArrayNode.of(List.of(flatArray));
var array2 = ArrayNode.of(List.of(array1, array1));
return array2;
}
public static DataStructureNode createTestData52() {
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.of(List.of(val));
return flatArray;
}
public static DataStructureNode createTestData53() {
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
var flatTuple = TupleNode.builder().add("key1", val).build();
var flatArray = ArrayNode.of(List.of(flatTuple, val));
return flatArray;
}
public static DataType createTestDataType5() {
return ArrayType.ofWildcard();
}
public static DataStructureNode createTestData61() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var array = ArrayNode.of(List.of(val, ValueNode.nullValue()));
var tuple = TupleNode.builder()
.add("key1", val).add("key2", array).build();
return tuple;
}
public static DataStructureNode createTestData62() {
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
var flatTuple = TupleNode.builder().add("key1", val).build();
var tuple = TupleNode.builder()
.add("key1", flatTuple).add("key2", val).build();
return tuple;
}
public static DataType createTestDataType6() {
return TupleType.of(List.of("key1", "key2"), List.of(WildcardType.of(), WildcardType.of()));
}
} }

View file

@ -1,35 +0,0 @@
package io.xpipe.core.test;
import io.xpipe.core.data.generic.GenericDataStreamParser;
import io.xpipe.core.data.generic.GenericDataStreamWriter;
import io.xpipe.core.data.generic.GenericDataStructureNodeReader;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HexFormat;
import static io.xpipe.core.test.DataStructureTests.createTestData;
public class GenericDataStructureIoTest {
@Test
public void testBasicIo() throws IOException {
var obj = createTestData();
var dataOut = new ByteArrayOutputStream();
GenericDataStreamWriter.write(dataOut, obj);
var data = dataOut.toByteArray();
var format = HexFormat.of().withPrefix("0x").withDelimiter(" ");
System.out.println(format.formatHex(data));
var reader = new GenericDataStructureNodeReader();
GenericDataStreamParser.read(new ByteArrayInputStream(data), reader);
var node = reader.create();
Assertions.assertEquals(obj, node);
System.out.println(node);
}
}

View file

@ -1,61 +0,0 @@
package io.xpipe.core.test;
import io.xpipe.core.data.typed.TypedDataStreamParser;
import io.xpipe.core.data.typed.TypedDataStreamWriter;
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HexFormat;
import static io.xpipe.core.test.DataStructureTests.createTestData;
public class TypedDataStructureIoTest {
@Test
public void testBasicIo() throws IOException {
var obj = createTestData();
var type = obj.getDataType();
var dataOut = new ByteArrayOutputStream();
TypedDataStreamWriter.writeStructure(dataOut, obj, type);
var data = dataOut.toByteArray();
var format = HexFormat.of().withPrefix("0x").withDelimiter(" ");
System.out.println(format.formatHex(data));
var reader = new TypedDataStructureNodeReader(type);
new TypedDataStreamParser(type).readStructure(new ByteArrayInputStream(data), reader);
var node = reader.create();
Assertions.assertEquals(obj, node);
System.out.println(node);
}
@Test
public void testBasicReusableIo() throws IOException {
var obj = createTestData();
var type = obj.getDataType();
var dataOut = new ByteArrayOutputStream();
TypedDataStreamWriter.writeStructure(dataOut, obj, type);
TypedDataStreamWriter.writeStructure(dataOut, obj, type);
var data = dataOut.toByteArray();
var format = HexFormat.of().withPrefix("0x").withDelimiter(" ");
System.out.println(format.formatHex(data));
var in = new ByteArrayInputStream(data);
var reader = new TypedReusableDataStructureNodeReader(type);
new TypedDataStreamParser(type).readStructure(in, reader);
var firstNode = reader.create();
new TypedDataStreamParser(type).readStructure(in, reader);
var secondNode = reader.create();
System.out.println(firstNode);
Assertions.assertEquals(obj, firstNode);
Assertions.assertEquals(obj, secondNode);
}
}

View file

@ -4,4 +4,5 @@ module io.xpipe.core.test {
requires org.junit.jupiter.api; requires org.junit.jupiter.api;
requires org.junit.jupiter.params; requires org.junit.jupiter.params;
requires io.xpipe.core; requires io.xpipe.core;
requires static lombok;
} }