mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
Rework mutability of nodes
This commit is contained in:
parent
cf89b2ff18
commit
562f02b607
25 changed files with 503 additions and 208 deletions
|
@ -99,7 +99,7 @@ public class DataTableImpl implements DataTable {
|
|||
}, false);
|
||||
}
|
||||
}.execute();
|
||||
return ArrayNode.wrap(nodes);
|
||||
return ArrayNode.of(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,6 +20,8 @@ dependencies {
|
|||
compileOnly '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-params:5.8.2'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
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 java.util.Iterator;
|
||||
|
@ -29,6 +32,10 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
|||
throw unsupported("set raw data");
|
||||
}
|
||||
|
||||
public DataStructureNode setNull() {
|
||||
throw unsupported("set null");
|
||||
}
|
||||
|
||||
public DataStructureNode set(int index, DataStructureNode node) {
|
||||
throw unsupported("set at index");
|
||||
}
|
||||
|
@ -47,6 +54,34 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
|||
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) {
|
||||
throw unsupported("put node with key");
|
||||
}
|
||||
|
@ -67,7 +102,7 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
|||
throw unsupported("size computation");
|
||||
}
|
||||
|
||||
public abstract DataType getDataType();
|
||||
public abstract DataType determineDataType();
|
||||
|
||||
public DataStructureNode at(int index) {
|
||||
throw unsupported("integer indexing");
|
||||
|
|
|
@ -92,7 +92,7 @@ public class GenericArrayReader implements GenericAbstractReader {
|
|||
throw new IllegalStateException("Array ended but is not full yet");
|
||||
}
|
||||
|
||||
created = ArrayNode.wrap(nodes);
|
||||
created = ArrayNode.of(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -142,7 +142,7 @@ public class GenericArrayReader implements GenericAbstractReader {
|
|||
throw new IllegalStateException("Array is full but got another value");
|
||||
}
|
||||
|
||||
put(ValueNode.wrap(value));
|
||||
put(ValueNode.mutable(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -156,6 +156,6 @@ public class GenericArrayReader implements GenericAbstractReader {
|
|||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
return ArrayNode.wrap(nodes);
|
||||
return ArrayNode.of(nodes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,6 @@ public class GenericDataStructureNodeReader implements GenericDataStreamCallback
|
|||
return;
|
||||
}
|
||||
|
||||
node = ValueNode.wrap(value);
|
||||
node = ValueNode.mutable(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ public class GenericTupleReader implements GenericAbstractReader {
|
|||
throw new IllegalStateException("Tuple is full but got another value");
|
||||
}
|
||||
|
||||
putNode(ValueNode.wrap(value));
|
||||
putNode(ValueNode.mutable(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,14 +19,14 @@ public class ArrayNode extends DataStructureNode {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static ArrayNode copy(List<DataStructureNode> valueNodes) {
|
||||
public static ArrayNode copyOf(List<DataStructureNode> valueNodes) {
|
||||
return new ArrayNode(new ArrayList<>(valueNodes));
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,8 @@ public class ArrayNode extends DataStructureNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ArrayType getDataType() {
|
||||
return ArrayType.of(valueNodes.stream().map(DataStructureNode::getDataType).toList());
|
||||
public ArrayType determineDataType() {
|
||||
return ArrayType.of(valueNodes.stream().map(DataStructureNode::determineDataType).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -17,8 +17,14 @@ public class NoKeyTupleNode extends TupleNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType() {
|
||||
return TupleType.wrap(null, nodes.stream().map(DataStructureNode::getDataType).toList());
|
||||
public DataStructureNode set(int index, DataStructureNode node) {
|
||||
nodes.set(index, node);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType determineDataType() {
|
||||
return TupleType.of(null, nodes.stream().map(DataStructureNode::determineDataType).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,8 +19,14 @@ public class SimpleTupleNode extends TupleNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType() {
|
||||
return TupleType.wrap(names, nodes.stream().map(DataStructureNode::getDataType).toList());
|
||||
public DataStructureNode set(int index, DataStructureNode node) {
|
||||
nodes.set(index, node);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType determineDataType() {
|
||||
return TupleType.of(names, nodes.stream().map(DataStructureNode::determineDataType).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,6 +56,7 @@ public class SimpleTupleNode extends TupleNode {
|
|||
@Override
|
||||
public DataStructureNode clear() {
|
||||
nodes.clear();
|
||||
names.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,59 +8,68 @@ import lombok.EqualsAndHashCode;
|
|||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@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) {
|
||||
this.data = data;
|
||||
public static ValueNode immutable(byte[] data) {
|
||||
return new ImmutableValueNode(data);
|
||||
}
|
||||
|
||||
public static ValueNode wrap(byte[] data) {
|
||||
return new ValueNode(data);
|
||||
public static ValueNode mutableNull() {
|
||||
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) {
|
||||
return new ValueNode(o.toString().getBytes(StandardCharsets.UTF_8));
|
||||
return mutable(o);
|
||||
}
|
||||
|
||||
protected ValueNode() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStructureNode setRawData(byte[] data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
public abstract DataStructureNode setRawData(byte[] data);
|
||||
|
||||
@Override
|
||||
public boolean isValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int asInt() {
|
||||
public final int asInt() {
|
||||
return Integer.parseInt(asString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String asString() {
|
||||
return new String(data);
|
||||
public final String asString() {
|
||||
return new String(getRawData());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName() {
|
||||
public final boolean isValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final String getName() {
|
||||
return "value node";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(int indent) {
|
||||
return new String(data);
|
||||
public final DataType determineDataType() {
|
||||
return ValueType.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType getDataType() {
|
||||
return new ValueType();
|
||||
}
|
||||
|
||||
public byte[] getRawData() {
|
||||
return data;
|
||||
}
|
||||
public abstract byte[] getRawData();
|
||||
}
|
||||
|
|
|
@ -11,14 +11,18 @@ import java.util.List;
|
|||
@EqualsAndHashCode
|
||||
public class ArrayType implements DataType {
|
||||
|
||||
public static ArrayType ofWildcard() {
|
||||
return new ArrayType(WildcardType.of());
|
||||
}
|
||||
|
||||
public static ArrayType of(List<DataType> types) {
|
||||
if (types.size() == 0) {
|
||||
return new ArrayType(new WildcardType());
|
||||
return new ArrayType(WildcardType.of());
|
||||
}
|
||||
|
||||
var first = types.get(0);
|
||||
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;
|
||||
|
|
|
@ -26,7 +26,7 @@ public class TupleType implements DataType {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,14 @@ import lombok.EqualsAndHashCode;
|
|||
@EqualsAndHashCode
|
||||
public class ValueType implements DataType {
|
||||
|
||||
public static ValueType of() {
|
||||
return new ValueType();
|
||||
}
|
||||
|
||||
private ValueType() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "value";
|
||||
|
|
|
@ -5,6 +5,14 @@ import io.xpipe.core.data.type.callback.DataTypeCallback;
|
|||
|
||||
public class WildcardType implements DataType {
|
||||
|
||||
public static WildcardType of() {
|
||||
return new WildcardType();
|
||||
}
|
||||
|
||||
private WildcardType() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "wildcard";
|
||||
|
|
|
@ -13,7 +13,7 @@ public interface DataTypeCallback {
|
|||
return new DataTypeCallback() {
|
||||
@Override
|
||||
public void onValue() {
|
||||
typeConsumer.accept(new ValueType());
|
||||
typeConsumer.accept(ValueType.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -50,7 +50,7 @@ public class TypedDataStreamWriter {
|
|||
if (!type.getTypes().get(i).isWildcard()) {
|
||||
write(out, tuple.at(i), type.getTypes().get(i));
|
||||
} else {
|
||||
GenericDataStreamWriter.write(out, tuple);
|
||||
GenericDataStreamWriter.write(out, tuple.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,25 +11,35 @@ import io.xpipe.core.data.type.callback.DataTypeCallback;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
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 final List<DataType> flattened;
|
||||
private Stack<List<DataStructureNode>> children;
|
||||
private Stack<DataStructureNode> nodes;
|
||||
private final Stack<List<DataStructureNode>> children;
|
||||
private final Stack<DataStructureNode> nodes;
|
||||
private DataStructureNode readNode;
|
||||
private boolean initialized;
|
||||
private int arrayDepth;
|
||||
private boolean makeImmutable;
|
||||
private final boolean makeImmutable;
|
||||
|
||||
public TypedDataStructureNodeReader(DataType type) {
|
||||
private TypedDataStructureNodeReader(DataType type, boolean makeImmutable) {
|
||||
flattened = new ArrayList<>();
|
||||
children = new Stack<>();
|
||||
nodes = new Stack<>();
|
||||
type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d)));
|
||||
this.makeImmutable = makeImmutable;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,55 +71,22 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
|
||||
@Override
|
||||
public void onValue(byte[] data) {
|
||||
var val = makeImmutable ? ValueNode.immutable(data) : ValueNode.mutable(data);
|
||||
if (!initialized) {
|
||||
readNode = ValueNode.wrap(data);
|
||||
readNode = val;
|
||||
return;
|
||||
}
|
||||
|
||||
children.peek().add(ValueNode.wrap(data));
|
||||
children.peek().add(val);
|
||||
if (!flattened.get(currentDataTypeIndex).isArray()) {
|
||||
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() {
|
||||
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
|
||||
public void onGenericNode(DataStructureNode node) {
|
||||
children.peek().add(node);
|
||||
|
@ -134,13 +111,32 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
|
||||
var l = new ArrayList<DataStructureNode>(tupleType.getSize());
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
|
@ -151,13 +147,26 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
|
||||
var l = new ArrayList<DataStructureNode>();
|
||||
children.push(l);
|
||||
var newNode = ArrayNode.wrap(l);
|
||||
|
||||
var arrayNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
|
||||
var newNode = ArrayNode.of(arrayNodes);
|
||||
nodes.push(newNode);
|
||||
arrayDepth++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onArrayEnd() {
|
||||
finishArray();
|
||||
arrayDepth--;
|
||||
if (!isInArray()) {
|
||||
currentDataTypeIndex++;
|
||||
}
|
||||
|
||||
children.pop();
|
||||
var popped = nodes.pop();
|
||||
if (nodes.empty()) {
|
||||
readNode = popped;
|
||||
} else {
|
||||
children.peek().add(popped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
public class TypedReusableDataStructureNodeReader implements TypedDataStreamCallback {
|
||||
public class TypedReusableDataStructureNodeReader implements TypedAbstractReader {
|
||||
|
||||
private TypedDataStructureNodeReader initialReader;
|
||||
private DataStructureNode node;
|
||||
|
@ -22,10 +22,15 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
|
|||
public TypedReusableDataStructureNodeReader(DataType type) {
|
||||
flattened = new ArrayList<>();
|
||||
indices = new Stack<>();
|
||||
initialReader = new TypedDataStructureNodeReader(type);
|
||||
initialReader = TypedDataStructureNodeReader.mutable(type);
|
||||
type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public DataStructureNode create() {
|
||||
return node;
|
||||
}
|
||||
|
@ -47,10 +52,12 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
|
|||
}
|
||||
|
||||
if (isInArray()) {
|
||||
getCurrentParent().set(indices.peek(), ValueNode.wrap(data));
|
||||
indices.push(indices.pop() + 1);
|
||||
getCurrentParent().set(indices.peek(), ValueNode.mutable(data));
|
||||
} else {
|
||||
getCurrent().setRawData(data);
|
||||
}
|
||||
|
||||
if (!indices.isEmpty()) {
|
||||
indices.push(indices.pop() + 1);
|
||||
}
|
||||
}
|
||||
|
@ -62,11 +69,8 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
|
|||
return;
|
||||
}
|
||||
|
||||
if (isInArray()) {
|
||||
getCurrentParent().set(indices.peek(), node);
|
||||
indices.push(indices.pop() + 1);
|
||||
} else {
|
||||
getCurrent().set(indices.peek(), node);
|
||||
if (!indices.isEmpty()) {
|
||||
indices.push(indices.pop() + 1);
|
||||
}
|
||||
}
|
||||
|
@ -140,8 +144,10 @@ public class TypedReusableDataStructureNodeReader implements TypedDataStreamCall
|
|||
|
||||
indices.pop();
|
||||
arrayDepth--;
|
||||
if (!indices.isEmpty()) {
|
||||
indices.push(indices.pop() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNodeBegin() {
|
||||
|
|
|
@ -1,22 +1,34 @@
|
|||
package io.xpipe.core.test;
|
||||
|
||||
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.TupleNode;
|
||||
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.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.util.List;
|
||||
|
||||
public class DataStructureTest {
|
||||
|
||||
public static DataStructureNode createTestData() {
|
||||
var val = ValueNode.wrap("value".getBytes(StandardCharsets.UTF_8));
|
||||
var flatArray = ArrayNode.wrap(List.of(ValueNode.of(1), ValueNode.of(2)));
|
||||
var val = ValueNode.of("value");
|
||||
var flatArray = ArrayNode.of(List.of(ValueNode.of(1), ValueNode.of(2)));
|
||||
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()
|
||||
.add("key1", val)
|
||||
.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).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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,47 @@ import io.xpipe.core.data.DataStructureNode;
|
|||
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.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class DataStructureTests {
|
||||
|
||||
public static DataStructureNode createTestData() {
|
||||
var val = ValueNode.wrap("value".getBytes(StandardCharsets.UTF_8));
|
||||
var flatArray = ArrayNode.wrap(List.of(ValueNode.of(1), ValueNode.of(2)));
|
||||
@AllArgsConstructor
|
||||
|
||||
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 nestedArray = ArrayNode.wrap(List.of(flatArray, flatTuple));
|
||||
var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
|
||||
return TupleNode.builder()
|
||||
.add("key1", val)
|
||||
.add("key2", flatArray)
|
||||
|
@ -22,4 +52,109 @@ public class DataStructureTests {
|
|||
.add("key4", nestedArray)
|
||||
.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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -4,4 +4,5 @@ module io.xpipe.core.test {
|
|||
requires org.junit.jupiter.api;
|
||||
requires org.junit.jupiter.params;
|
||||
requires io.xpipe.core;
|
||||
requires static lombok;
|
||||
}
|
Loading…
Reference in a new issue