Rework mutability

This commit is contained in:
Christopher Schnick 2021-12-19 15:34:58 +01:00
parent df9495e0a3
commit b54d8ad362
21 changed files with 486 additions and 278 deletions

View file

@ -10,71 +10,75 @@ import java.util.List;
public class GenericDataStreamParser {
public static DataStructureNode read(InputStream in) throws IOException {
public static DataStructureNode parse(InputStream in) throws IOException {
var reader = new GenericDataStructureNodeReader();
read(in, reader);
parse(in, reader);
return reader.create();
}
public static List<DataStructureNode> readN(InputStream in, int n) throws IOException {
public static List<DataStructureNode> parseN(InputStream in, int n) throws IOException {
var list = new ArrayList<DataStructureNode>();
var reader = new GenericDataStructureNodeReader();
for (int i = 0; i < n; i++) {
read(in, reader);
parse(in, reader);
list.add(reader.create());
}
return list;
}
public static void read(InputStream in, GenericDataStreamCallback cb) throws IOException {
public static void parse(InputStream in, GenericDataStreamCallback cb) throws IOException {
var b = in.read();
if (b == -1) {
return;
}
if (b == DataStructureNodeIO.GENERIC_STRUCTURE_ID) {
b = in.read();
}
switch (b) {
case DataStructureNodeIO.GENERIC_TUPLE_ID -> {
readTuple(in, cb);
parseTuple(in, cb);
}
case DataStructureNodeIO.GENERIC_ARRAY_ID -> {
readArray(in, cb);
parseArray(in, cb);
}
case DataStructureNodeIO.GENERIC_VALUE_ID -> {
readValue(in, cb);
parseValue(in, cb);
}
case DataStructureNodeIO.GENERIC_NAME_ID -> {
readName(in, cb);
read(in, cb);
parseName(in, cb);
parse(in, cb);
}
default -> throw new IllegalStateException("Unexpected type id: " + b);
}
}
private static void readName(InputStream in, GenericDataStreamCallback cb) throws IOException {
private static void parseName(InputStream in, GenericDataStreamCallback cb) throws IOException {
var nameLength = in.read();
var name = new String(in.readNBytes(nameLength));
cb.onName(name);
}
private static void readTuple(InputStream in, GenericDataStreamCallback cb) throws IOException {
private static void parseTuple(InputStream in, GenericDataStreamCallback cb) throws IOException {
var size = in.read();
cb.onTupleStart(size);
for (int i = 0; i < size; i++) {
read(in, cb);
parse(in, cb);
}
cb.onTupleEnd();
}
private static void readArray(InputStream in, GenericDataStreamCallback cb) throws IOException {
private static void parseArray(InputStream in, GenericDataStreamCallback cb) throws IOException {
var size = in.read();
cb.onArrayStart(size);
for (int i = 0; i < size; i++) {
read(in, cb);
parse(in, cb);
}
cb.onArrayEnd();
}
private static void readValue(InputStream in, GenericDataStreamCallback cb) throws IOException {
private static void parseValue(InputStream in, GenericDataStreamCallback cb) throws IOException {
var size = in.read();
var data = in.readNBytes(size);
cb.onValue(data);

View file

@ -1,9 +1,6 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.node.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.node.*;
import java.io.IOException;
import java.io.OutputStream;
@ -11,12 +8,12 @@ import java.nio.charset.StandardCharsets;
public class GenericDataStreamWriter {
private static final int TUPLE_ID = 1;
private static final int ARRAY_ID = 2;
private static final int VALUE_ID = 3;
private static final int NAME_ID = 4;
public static void writeStructure(OutputStream out, DataStructureNode node) throws IOException {
out.write(DataStructureNodeIO.GENERIC_STRUCTURE_ID);
write(out, node);
}
public static void write(OutputStream out, DataStructureNode node) throws IOException {
private static void write(OutputStream out, DataStructureNode node) throws IOException {
if (node.isTuple()) {
writeTuple(out, (TupleNode) node);
} else if (node.isArray()) {
@ -29,23 +26,25 @@ public class GenericDataStreamWriter {
}
private static void writeName(OutputStream out, String s) throws IOException {
var b = s.getBytes(StandardCharsets.UTF_8);
out.write(NAME_ID);
out.write(b.length);
out.write(b);
if (s != null) {
var b = s.getBytes(StandardCharsets.UTF_8);
out.write(DataStructureNodeIO.GENERIC_NAME_ID);
out.write(b.length);
out.write(b);
}
}
private static void writeTuple(OutputStream out, TupleNode tuple) throws IOException {
out.write(TUPLE_ID);
out.write(DataStructureNodeIO.GENERIC_TUPLE_ID);
out.write(tuple.size());
for (int i = 0; i < tuple.size(); i++) {
writeName(out, tuple.nameAt(i));
writeName(out, tuple.keyNameAt(i));
write(out, tuple.at(i));
}
}
private static void writeArray(OutputStream out, ArrayNode array) throws IOException {
out.write(ARRAY_ID);
out.write(DataStructureNodeIO.GENERIC_ARRAY_ID);
out.write(array.size());
for (int i = 0; i < array.size(); i++) {
write(out, array.at(i));
@ -53,7 +52,7 @@ public class GenericDataStreamWriter {
}
private static void writeValue(OutputStream out, ValueNode value) throws IOException {
out.write(VALUE_ID);
out.write(DataStructureNodeIO.GENERIC_VALUE_ID);
out.write(value.getRawData().length);
out.write(value.getRawData());
}

View file

@ -42,6 +42,11 @@ public class GenericTupleReader implements GenericAbstractReader {
}
private void putNode(DataStructureNode node) {
// If no key was read, assume null key
if (this.names.size() == this.nodes.size()) {
this.names.add(null);
}
this.nodes.add(node);
currentIndex++;
}

View file

@ -1,100 +1,66 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.type.ArrayType;
import lombok.EqualsAndHashCode;
import java.util.*;
import java.util.function.Consumer;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@EqualsAndHashCode(callSuper = false)
public class ArrayNode extends DataStructureNode {
private final List<DataStructureNode> valueNodes;
private ArrayNode(List<DataStructureNode> valueNodes) {
this.valueNodes = valueNodes;
}
public abstract class ArrayNode extends DataStructureNode {
public static ArrayNode of(DataStructureNode... dsn) {
return of(List.of(dsn));
}
public static ArrayNode of(List<DataStructureNode> valueNodes) {
return new ArrayNode(valueNodes);
public static ArrayNode of(List<DataStructureNode> nodes) {
return new SimpleArrayNode(true, nodes);
}
public static ArrayNode copyOf(List<DataStructureNode> valueNodes) {
return new ArrayNode(new ArrayList<>(valueNodes));
public static ArrayNode of(boolean mutable, List<DataStructureNode> nodes) {
return new SimpleArrayNode(mutable, nodes);
}
protected ArrayNode() {
}
@Override
public DataStructureNode put(DataStructureNode node) {
valueNodes.add(node);
return this;
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ArrayNode that)) return false;
return getNodes().equals(that.getNodes());
}
@Override
public DataStructureNode set(int index, DataStructureNode node) {
valueNodes.add(index, node);
return this;
public int hashCode() {
return Objects.hash(getNodes());
}
@Override
public Stream<DataStructureNode> stream() {
return Collections.unmodifiableList(valueNodes).stream();
}
@Override
public boolean isArray() {
public final boolean isArray() {
return true;
}
@Override
public int size() {
return valueNodes.size();
}
@Override
protected String getName() {
protected final String getName() {
return "array node";
}
@Override
public String toString(int indent) {
var content = valueNodes.stream().map(n -> n.toString(indent)).collect(Collectors.joining(", "));
return "[" + content + "]";
public abstract ArrayNode immutableView();
@Override
public abstract ArrayNode mutableCopy();
protected abstract String getIdentifier();
@Override
public final String toString(int indent) {
var content = getNodes().stream().map(n -> n.toString(indent)).collect(Collectors.joining(", "));
return "(" + getIdentifier() + ") [" + content + "]";
}
@Override
public ArrayType determineDataType() {
return ArrayType.ofSharedType(valueNodes.stream().map(DataStructureNode::determineDataType).toList());
}
@Override
public DataStructureNode clear() {
valueNodes.clear();
return this;
}
@Override
public DataStructureNode at(int index) {
return valueNodes.get(index);
}
@Override
public void forEach(Consumer<? super DataStructureNode> action) {
valueNodes.forEach(action);
}
@Override
public Spliterator<DataStructureNode> spliterator() {
return valueNodes.spliterator();
}
@Override
public Iterator<DataStructureNode> iterator() {
return valueNodes.iterator();
public final ArrayType determineDataType() {
return ArrayType.ofSharedType(getNodes().stream().map(DataStructureNode::determineDataType).toList());
}
}

View file

@ -3,6 +3,7 @@ package io.xpipe.core.data.node;
import io.xpipe.core.data.type.DataType;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Spliterator;
import java.util.function.Consumer;
@ -10,12 +11,37 @@ import java.util.stream.Stream;
public abstract class DataStructureNode implements Iterable<DataStructureNode> {
public abstract DataStructureNode mutableCopy();
public String keyNameAt(int index) {
throw unsupported("key name at");
}
public List<KeyValue> getKeyValuePairs() {
throw unsupported("get key value pairs");
}
public List<String> getKeyNames() {
throw unsupported("get key names");
}
public List<DataStructureNode> getNodes() {
throw unsupported("get nodes");
}
public record KeyValue(String key, DataStructureNode value) {
}
protected abstract String getName();
protected UnsupportedOperationException unsupported(String s) {
return new UnsupportedOperationException(getName() + " does not support " + s);
}
public abstract boolean isMutable();
public abstract DataStructureNode immutableView();
@Override
public String toString() {
return toString(0);

View file

@ -10,7 +10,22 @@ public class ImmutableValueNode extends ValueNode {
@Override
public String toString(int indent) {
return getClass().getSimpleName() + "(" + new String(data) + ")";
return new String(data) + "(I)";
}
@Override
public boolean isMutable() {
return false;
}
@Override
public ValueNode immutableView() {
return this;
}
@Override
public ValueNode mutableCopy() {
return ValueNode.mutable(data);
}
@Override

View file

@ -10,7 +10,22 @@ public class MutableValueNode extends ValueNode {
@Override
public String toString(int indent) {
return getClass().getSimpleName() + "(" + new String(data) + ")";
return new String(data) + "(M)";
}
@Override
public boolean isMutable() {
return true;
}
@Override
public ValueNode immutableView() {
return new ImmutableValueNode(data);
}
@Override
public ValueNode mutableCopy() {
return new MutableValueNode(data);
}
@Override

View file

@ -3,27 +3,46 @@ package io.xpipe.core.data.node;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class NoKeyTupleNode extends TupleNode {
private final boolean mutable;
private final List<DataStructureNode> nodes;
NoKeyTupleNode(List<DataStructureNode> nodes) {
this.nodes = nodes;
NoKeyTupleNode(boolean mutable, List<DataStructureNode> nodes) {
this.mutable = mutable;
this.nodes = mutable ? nodes : Collections.unmodifiableList(nodes);
}
@Override
public TupleNode mutableCopy() {
return new NoKeyTupleNode(true, nodes.stream()
.map(DataStructureNode::mutableCopy)
.collect(Collectors.toCollection(ArrayList::new)));
}
@Override
public TupleNode immutableView() {
return new NoKeyTupleNode(false, nodes.stream()
.map(DataStructureNode::immutableView)
.collect(Collectors.toCollection(ArrayList::new)));
}
@Override
public DataStructureNode set(int index, DataStructureNode node) {
checkMutable();
nodes.set(index, node);
return this;
}
@Override
public DataType determineDataType() {
return TupleType.of(null, nodes.stream().map(DataStructureNode::determineDataType).toList());
return TupleType.of(nodes.stream().map(DataStructureNode::determineDataType).toList());
}
@Override
@ -31,40 +50,37 @@ public class NoKeyTupleNode extends TupleNode {
return "no key tuple node";
}
@Override
public boolean isMutable() {
return mutable;
}
@Override
public DataStructureNode at(int index) {
return nodes.get(index);
}
@Override
public DataStructureNode forKey(String name) {
throw unsupported("key indexing");
}
@Override
public Optional<DataStructureNode> forKeyIfPresent(String name) {
return Optional.empty();
}
@Override
public int size() {
return nodes.size();
}
public String nameAt(int index) {
throw unsupported("name getter");
}
@Override
public List<KeyValue> getKeyValuePairs() {
return nodes.stream().map(n -> new KeyValue(null, n)).toList();
}
public List<String> getNames() {
@Override
public List<String> getKeyNames() {
return Collections.nCopies(size(), null);
}
public List<DataStructureNode> getNodes() {
return Collections.unmodifiableList(nodes);
return nodes;
}
@Override
protected String getIdentifier() {
return "NK";
}
}

View file

@ -0,0 +1,114 @@
package io.xpipe.core.data.node;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SimpleArrayNode extends ArrayNode {
private final boolean mutable;
private final List<DataStructureNode> nodes;
SimpleArrayNode(boolean mutable, List<DataStructureNode> nodes) {
this.nodes = nodes;
this.mutable = mutable;
}
private void checkMutable() {
if (!mutable) {
throw new UnsupportedOperationException("Array node is immutable");
}
}
@Override
public DataStructureNode put(DataStructureNode node) {
checkMutable();
nodes.add(node);
return this;
}
@Override
public DataStructureNode set(int index, DataStructureNode node) {
checkMutable();
nodes.add(index, node);
return this;
}
@Override
public Stream<DataStructureNode> stream() {
return nodes.stream();
}
@Override
public int size() {
return nodes.size();
}
@Override
public ArrayNode mutableCopy() {
return new SimpleArrayNode(true, nodes.stream()
.map(DataStructureNode::mutableCopy)
.collect(Collectors.toCollection(ArrayList::new)));
}
@Override
protected String getIdentifier() {
return "S";
}
@Override
public boolean isMutable() {
return mutable;
}
@Override
public ArrayNode immutableView() {
return new SimpleArrayNode(false, nodes.stream()
.map(DataStructureNode::immutableView)
.collect(Collectors.toCollection(ArrayList::new)));
}
@Override
public DataStructureNode clear() {
checkMutable();
nodes.clear();
return this;
}
@Override
public DataStructureNode at(int index) {
return nodes.get(index);
}
@Override
public void forEach(Consumer<? super DataStructureNode> action) {
nodes.forEach(action);
}
@Override
public Spliterator<DataStructureNode> spliterator() {
return nodes.spliterator();
}
@Override
public Iterator<DataStructureNode> iterator() {
return nodes.iterator();
}
@Override
public List<DataStructureNode> getNodes() {
return nodes;
}
@Override
public DataStructureNode remove(int index) {
checkMutable();
nodes.remove(index);
return this;
}
}

View file

@ -2,26 +2,42 @@ package io.xpipe.core.data.node;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
@EqualsAndHashCode(callSuper = false)
public class SimpleTupleNode extends TupleNode {
private final boolean mutable;
private final List<String> names;
private final List<DataStructureNode> nodes;
SimpleTupleNode(List<String> names, List<DataStructureNode> nodes) {
this.names = names;
this.nodes = nodes;
SimpleTupleNode(boolean mutable, List<String> names, List<DataStructureNode> nodes) {
this.mutable = mutable;
this.names = mutable ? names : Collections.unmodifiableList(names);
this.nodes = mutable ? nodes : Collections.unmodifiableList(nodes);
}
@Override
public TupleNode mutableCopy() {
var nodesCopy = nodes.stream()
.map(DataStructureNode::mutableCopy)
.collect(Collectors.toCollection(ArrayList::new));
return new SimpleTupleNode(true, new ArrayList<>(names), nodesCopy);
}
@Override
public TupleNode immutableView() {
var nodesCopy = nodes.stream()
.map(DataStructureNode::immutableView)
.collect(Collectors.toCollection(ArrayList::new));
return new SimpleTupleNode(false, names, nodesCopy);
}
@Override
public DataStructureNode set(int index, DataStructureNode node) {
checkMutable();
nodes.set(index, node);
return this;
}
@ -36,6 +52,11 @@ public class SimpleTupleNode extends TupleNode {
return "tuple node";
}
@Override
public boolean isMutable() {
return mutable;
}
@Override
public DataStructureNode at(int index) {
return nodes.get(index);
@ -57,6 +78,8 @@ public class SimpleTupleNode extends TupleNode {
@Override
public DataStructureNode clear() {
checkMutable();
nodes.clear();
names.clear();
return this;
@ -68,7 +91,7 @@ public class SimpleTupleNode extends TupleNode {
return nodes.size();
}
public String nameAt(int index) {
public String keyNameAt(int index) {
return names.get(index);
}
@ -76,16 +99,21 @@ public class SimpleTupleNode extends TupleNode {
public List<KeyValue> getKeyValuePairs() {
var l = new ArrayList<KeyValue>(size());
for (int i = 0; i < size(); i++) {
l.add(new KeyValue(getNames().get(i), getNodes().get(i)));
l.add(new KeyValue(getKeyNames().get(i), getNodes().get(i)));
}
return l;
}
public List<String> getNames() {
public List<String> getKeyNames() {
return Collections.unmodifiableList(names);
}
public List<DataStructureNode> getNodes() {
return Collections.unmodifiableList(nodes);
}
@Override
protected String getIdentifier() {
return "S";
}
}

View file

@ -1,7 +1,5 @@
package io.xpipe.core.data.node;
import lombok.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -18,11 +16,7 @@ public abstract class TupleNode extends DataStructureNode {
throw new IllegalArgumentException("Nodes must be not null");
}
return new NoKeyTupleNode(nodes);
}
public static TupleNode copyOf(List<DataStructureNode> nodes) {
return TupleNode.of(List.copyOf(nodes));
return new NoKeyTupleNode(true, nodes);
}
public static TupleNode of(List<String> names, List<DataStructureNode> nodes) {
@ -36,55 +30,62 @@ public abstract class TupleNode extends DataStructureNode {
throw new IllegalArgumentException("Names and nodes must have the same length");
}
return new SimpleTupleNode(names, nodes);
return new SimpleTupleNode(true, names, nodes);
}
public static TupleNode ofRaw(List<String> names, List<DataStructureNode> nodes) {
public static TupleNode of(boolean mutable, List<String> names, List<DataStructureNode> nodes) {
if (names == null) {
throw new IllegalArgumentException("Names must be not null");
}
if (nodes == null) {
throw new IllegalArgumentException("Nodes must be not null");
}
return new SimpleTupleNode(names, nodes);
}
public static TupleNode copyOf(List<String> names, List<DataStructureNode> nodes) {
return TupleNode.of(List.copyOf(names), List.copyOf(nodes));
return new SimpleTupleNode(mutable, names, nodes);
}
public final boolean isTuple() {
return true;
}
protected abstract String getIdentifier();
protected void checkMutable() {
if (!isMutable()) {
throw new UnsupportedOperationException("Tuple node is immutable");
}
}
@Override
public abstract TupleNode mutableCopy();
@Override
public abstract TupleNode immutableView();
@Override
public String toString(int indent) {
var is = " ".repeat(indent);
var start = getClass().getSimpleName() + " {\n";
var start = "(" + getIdentifier() + ") {\n";
var kvs = getKeyValuePairs().stream().map(kv -> {
if (kv.key == null) {
return is + " " + kv.value.toString(indent + 1) + "\n";
if (kv.key() == null) {
return is + " " + kv.value().toString(indent + 1) + "\n";
} else {
return is + " " + kv.key + "=" + kv.value.toString(indent + 1) + "\n";
return is + " " + kv.key() + "=" + kv.value().toString(indent + 1) + "\n";
}
}).collect(Collectors.joining());
var end = is + "}";
return start + kvs + end;
}
public abstract String nameAt(int index);
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TupleNode that)) return false;
return getKeyNames().equals(that.getKeyNames()) && getNodes().equals(that.getNodes());
}
public abstract List<KeyValue> getKeyValuePairs();
public abstract List<String> getNames();
public abstract List<DataStructureNode> getNodes();
@Value
public static class KeyValue {
String key;
DataStructureNode value;
@Override
public int hashCode() {
return Objects.hash(getKeyNames(), getNodes());
}
public static class Builder {
@ -104,11 +105,11 @@ public abstract class TupleNode extends DataStructureNode {
}
public TupleNode build() {
boolean hasKeys = entries.stream().anyMatch(kv -> kv.key != null);
boolean hasKeys = entries.stream().anyMatch(kv -> kv.key() != null);
return hasKeys ? TupleNode.of(
entries.stream().map(kv -> kv.key).toList(),
entries.stream().map(kv -> kv.value).toList()) :
TupleNode.of(entries.stream().map(kv -> kv.value).toList());
entries.stream().map(KeyValue::key).toList(),
entries.stream().map(KeyValue::value).toList()) :
TupleNode.of(entries.stream().map(KeyValue::value).toList());
}
}
}

View file

@ -2,11 +2,10 @@ package io.xpipe.core.data.node;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.ValueType;
import lombok.EqualsAndHashCode;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@EqualsAndHashCode(callSuper = false)
public abstract class ValueNode extends DataStructureNode {
private static final byte[] NULL = new byte[]{0};
@ -14,6 +13,24 @@ public abstract class ValueNode extends DataStructureNode {
protected ValueNode() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ValueNode that)) return false;
return Arrays.equals(getRawData(), that.getRawData());
}
@Override
public int hashCode() {
return Arrays.hashCode(getRawData());
}
@Override
public abstract ValueNode immutableView();
@Override
public abstract ValueNode mutableCopy();
public static ValueNode immutable(byte[] data) {
return new ImmutableValueNode(data);
}

View file

@ -27,6 +27,7 @@ public class DataTypeVisitors {
@Override
public void onArray(ArrayType type) {
typeConsumer.accept(type);
typeConsumer.accept(type.getSharedType());
}
@Override

View file

@ -68,11 +68,11 @@ public class TupleType extends DataType {
int counter = 0;
for (var kv : t.getKeyValuePairs()) {
if (!Objects.equals(kv.getKey(), names.get(counter))) {
if (!Objects.equals(kv.key(), names.get(counter))) {
return false;
}
if (!types.get(counter).matches(kv.getValue())) {
if (!types.get(counter).matches(kv.value())) {
return false;
}
counter++;

View file

@ -3,8 +3,6 @@ package io.xpipe.core.data.typed;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.type.TupleType;
import java.io.IOException;
public interface TypedDataStreamCallback {
default void onValue(byte[] data) {
@ -19,7 +17,7 @@ public interface TypedDataStreamCallback {
default void onTupleEnd() {
}
default void onArrayBegin(int size) throws IOException {
default void onArrayBegin(int size) {
}
default void onArrayEnd() {

View file

@ -90,9 +90,16 @@ public class TypedDataStreamParser {
parseValue(in, cb);
}
case DataStructureNodeIO.GENERIC_STRUCTURE_ID -> {
if (!type.isWildcard()) {
throw new IllegalStateException("Got structure but expected " + type.getName());
}
GenericDataStreamParser.parse(in, getGenericReader());
cb.onGenericNode(getGenericReader().create());
}
default -> {
GenericDataStreamParser.
throw new IllegalStateException("Unexpected value: " + b);
throw new IllegalStateException("Unexpected type id: " + b);
}
}
}
@ -100,14 +107,7 @@ public class TypedDataStreamParser {
private void parseTypedTuple(InputStream in, TypedDataStreamCallback cb, TupleType type) throws IOException {
cb.onTupleBegin(type);
for (int i = 0; i < type.getSize(); i++) {
if (type.getTypes().get(i).isWildcard()) {
var r = getGenericReader();
GenericDataStreamParser.read(in, r);
var node = r.create();
cb.onGenericNode(node);
} else {
parse(in, cb, type.getTypes().get(i));
}
parse(in, cb, type.getTypes().get(i));
}
cb.onTupleEnd();
}
@ -123,14 +123,7 @@ public class TypedDataStreamParser {
var size = in.read();
cb.onArrayBegin(size);
for (int i = 0; i < size; i++) {
if (type.getSharedType().isWildcard()) {
var r = getGenericReader();
GenericDataStreamParser.read(in, r);
var node = r.create();
cb.onGenericNode(node);
} else {
parse(in, cb, type.getSharedType());
}
parse(in, cb, type.getSharedType());
}
cb.onArrayEnd();
}

View file

@ -28,7 +28,7 @@ public class TypedDataStreamWriter {
} else if (node.isValue() && type.isValue()) {
writeValue(out, (ValueNode) node);
} else if (type.isWildcard()) {
GenericDataStreamWriter.write(out, node);
GenericDataStreamWriter.writeStructure(out, node);
} else {
throw new IllegalStateException("Incompatible node and type");
}

View file

@ -1,15 +1,10 @@
package io.xpipe.core.data.typed;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.node.*;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.type.DataTypeVisitors;
import io.xpipe.core.data.type.TupleType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -17,22 +12,6 @@ import java.util.Stack;
public class TypedDataStructureNodeReader implements TypedAbstractReader {
private final List<DataType> flattened;
private final Stack<List<DataStructureNode>> children;
private final Stack<DataStructureNode> nodes;
private final boolean makeImmutable;
private int currentDataTypeIndex;
private DataStructureNode readNode;
private boolean initialized;
private int arrayDepth;
private TypedDataStructureNodeReader(DataType type, boolean makeImmutable) {
flattened = new ArrayList<>();
children = new Stack<>();
nodes = new Stack<>();
type.visit(DataTypeVisitors.flatten(d -> flattened.add(d)));
this.makeImmutable = makeImmutable;
}
public static TypedDataStructureNodeReader mutable(DataType type) {
return new TypedDataStructureNodeReader(type, false);
}
@ -41,10 +20,30 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
return new TypedDataStructureNodeReader(type, true);
}
private final boolean makeImmutable;
private DataStructureNode readNode;
private final Stack<List<DataStructureNode>> children;
private final Stack<DataStructureNode> nodes;
private int arrayDepth;
private final List<DataType> flattened;
private DataType expectedType;
private int currentExpectedTypeIndex;
private TypedDataStructureNodeReader(DataType type, boolean makeImmutable) {
flattened = new ArrayList<>();
type.visit(DataTypeVisitors.flatten(flattened::add));
children = new Stack<>();
nodes = new Stack<>();
this.makeImmutable = makeImmutable;
expectedType = flattened.get(0);
}
@Override
public void onNodeBegin() {
if (nodes.size() != 0 || children.size() != 0) {
throw new IllegalStateException();
throw new IllegalStateException("Reader did not completely reset");
}
readNode = null;
@ -56,30 +55,37 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
}
public DataStructureNode create() {
if (readNode == null) {
throw new IllegalStateException("Reader is not finished yet");
}
return readNode;
}
@Override
public void onNodeEnd() {
if (nodes.size() != 0 || children.size() != 0 || readNode == null) {
throw new IllegalStateException();
throw new IllegalStateException("Reader is not finished yet");
}
}
initialized = false;
private void finishNode(DataStructureNode node) {
if (nodes.empty()) {
readNode = node;
} else {
children.peek().add(node);
}
}
@Override
public void onValue(byte[] data) {
var val = makeImmutable ? ValueNode.immutable(data) : ValueNode.mutable(data);
if (!initialized) {
readNode = val;
return;
if (!expectedType.isValue()) {
throw new IllegalStateException("Expected " + expectedType.getName() + " but got value");
}
children.peek().add(val);
if (!flattened.get(currentDataTypeIndex).isArray()) {
currentDataTypeIndex++;
}
var val = makeImmutable ? ValueNode.immutable(data) : ValueNode.mutable(data);
finishNode(val);
moveExpectedType(false);
}
private boolean isInArray() {
@ -88,25 +94,22 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
@Override
public void onGenericNode(DataStructureNode node) {
children.peek().add(node);
if (!isInArray()) {
currentDataTypeIndex++;
if (!expectedType.isWildcard()) {
throw new IllegalStateException("Expected " + expectedType.getName() + " but got generic node");
}
finishNode(makeImmutable ? node.immutableView() : node);
moveExpectedType(false);
}
@Override
public void onTupleBegin(TupleType type) {
if (!isInArray() && !flattened.get(currentDataTypeIndex).isTuple()) {
throw new IllegalStateException();
if (!expectedType.isTuple()) {
throw new IllegalStateException("Expected " + expectedType.getName() + " but got tuple");
}
TupleType tupleType = (TupleType) flattened.get(currentDataTypeIndex);
if (!initialized || !flattened.get(currentDataTypeIndex).isArray()) {
currentDataTypeIndex++;
}
if (!initialized) {
initialized = true;
}
TupleType tupleType = expectedType.asTuple();
moveExpectedType(false);
var l = new ArrayList<DataStructureNode>(tupleType.getSize());
children.push(l);
@ -114,7 +117,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
var tupleNames = makeImmutable ?
Collections.unmodifiableList(tupleType.getNames()) : new ArrayList<>(tupleType.getNames());
var tupleNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
var newNode = TupleNode.ofRaw(tupleNames, tupleNodes);
var newNode = TupleNode.of(!makeImmutable, tupleNames, tupleNodes);
nodes.push(newNode);
}
@ -123,49 +126,52 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
children.pop();
var popped = nodes.pop();
if (!popped.isTuple()) {
throw new IllegalStateException();
throw new IllegalStateException("No tuple to end");
}
SimpleTupleNode tuple = (SimpleTupleNode) popped;
if (tuple.getNames().size() != tuple.getNodes().size()) {
throw new IllegalStateException("");
TupleNode tuple = popped.asTuple();
if (tuple.getKeyNames().size() != tuple.getNodes().size()) {
throw new IllegalStateException("Tuple node size mismatch");
}
if (nodes.empty()) {
readNode = popped;
} else {
children.peek().add(popped);
finishNode(popped);
}
private void moveExpectedType(boolean force) {
if (!isInArray() || force) {
currentExpectedTypeIndex++;
expectedType = currentExpectedTypeIndex == flattened.size() ? null : flattened.get(currentExpectedTypeIndex);
}
}
@Override
public void onArrayBegin(int size) throws IOException {
if (!flattened.get(currentDataTypeIndex).isArray()) {
throw new IllegalStateException();
public void onArrayBegin(int size) {
if (!expectedType.isArray()) {
throw new IllegalStateException("Expected " + expectedType.getName() + " but got array");
}
arrayDepth++;
moveExpectedType(true);
var l = new ArrayList<DataStructureNode>();
children.push(l);
var arrayNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
var newNode = ArrayNode.of(arrayNodes);
nodes.push(newNode);
arrayDepth++;
}
@Override
public void onArrayEnd() {
arrayDepth--;
if (!isInArray()) {
currentDataTypeIndex++;
throw new IllegalStateException("No array to end");
}
arrayDepth--;
moveExpectedType(true);
children.pop();
var popped = nodes.pop();
if (nodes.empty()) {
readNode = popped;
} else {
children.peek().add(popped);
}
finishNode(popped);
}
}

View file

@ -3,10 +3,9 @@ package io.xpipe.core.data.typed;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.type.DataTypeVisitors;
import io.xpipe.core.data.type.TupleType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
@ -43,7 +42,6 @@ public class TypedReusableDataStructureNodeReader implements TypedAbstractReader
return arrayDepth >= 1;
}
private boolean initialized() {
return node != null;
}
@ -73,13 +71,25 @@ public class TypedReusableDataStructureNodeReader implements TypedAbstractReader
return;
}
getCurrentParent().set(indices.peek(), node);
if (hasParent()) {
getCurrentParent().set(indices.peek(), node);
} else {
this.node = node;
}
if (!indices.isEmpty()) {
indices.push(indices.pop() + 1);
}
}
private boolean hasParent() {
return indices.size() > 0;
}
private DataStructureNode getCurrentParent() {
if (!hasParent()) {
throw new IllegalStateException("No parent available");
}
var current = node;
for (var index : indices.subList(0, indices.size() - 1)) {
current = current.at(index);
@ -95,15 +105,6 @@ public class TypedReusableDataStructureNodeReader implements TypedAbstractReader
return current;
}
private void setValue(byte[] data) {
var current = node;
for (var index : indices) {
current = current.at(index);
}
var value = (ValueNode) current;
value.setRawData(data);
}
@Override
public void onTupleBegin(TupleType type) {
if (!initialized()) {
@ -128,7 +129,7 @@ public class TypedReusableDataStructureNodeReader implements TypedAbstractReader
}
@Override
public void onArrayBegin(int size) throws IOException {
public void onArrayBegin(int size) {
if (!initialized()) {
initialReader.onArrayBegin(size);
return;
@ -157,7 +158,6 @@ public class TypedReusableDataStructureNodeReader implements TypedAbstractReader
public void onNodeBegin() {
if (!initialized()) {
initialReader.onNodeBegin();
return;
}
}

View file

@ -79,10 +79,10 @@ public class DataStructureTest {
public void testGenericIo(DataStructureTests.TypedDataset ds) throws IOException {
for (var el : ds.nodes) {
var dataOut = new ByteArrayOutputStream();
GenericDataStreamWriter.write(dataOut, el);
GenericDataStreamWriter.writeStructure(dataOut, el);
var data = dataOut.toByteArray();
var reader = new GenericDataStructureNodeReader();
GenericDataStreamParser.read(new ByteArrayInputStream(data), reader);
GenericDataStreamParser.parse(new ByteArrayInputStream(data), reader);
var readNode = reader.create();
Assertions.assertEquals(el, readNode);

View file

@ -8,6 +8,7 @@ import io.xpipe.core.data.type.*;
import lombok.AllArgsConstructor;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class DataStructureTests {
@ -144,7 +145,7 @@ public class DataStructureTests {
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();
.add(val).add("key2", array).build();
return tuple;
}
@ -153,12 +154,15 @@ public class DataStructureTests {
var flatTuple = TupleNode.builder().add("key1", val).build();
var tuple = TupleNode.builder()
.add("key1", flatTuple).add("key2", val).build();
.add(flatTuple).add("key2", val).build();
return tuple;
}
public static DataType createTestDataType6() {
return TupleType.of(List.of("key1", "key2"), List.of(WildcardType.of(), WildcardType.of()));
var keys = new ArrayList<String>();
keys.add(null);
keys.add("key2");
return TupleType.of(keys, List.of(WildcardType.of(), WildcardType.of()));
}
public static DataStructureNode createTestData71() {