mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-10-02 18:17:27 +13:00
Rework data structure nodes, other rework
This commit is contained in:
parent
c7606f9f8e
commit
4d168b66c0
38 changed files with 408 additions and 762 deletions
|
@ -11,8 +11,10 @@ import io.xpipe.core.data.node.TupleNode;
|
||||||
import io.xpipe.core.data.typed.TypedAbstractReader;
|
import io.xpipe.core.data.typed.TypedAbstractReader;
|
||||||
import io.xpipe.core.data.typed.TypedDataStreamParser;
|
import io.xpipe.core.data.typed.TypedDataStreamParser;
|
||||||
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
|
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
|
||||||
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
|
import io.xpipe.core.source.DataSourceId;
|
||||||
import io.xpipe.core.source.*;
|
import io.xpipe.core.source.DataSourceInfo;
|
||||||
|
import io.xpipe.core.source.DataSourceReference;
|
||||||
|
import io.xpipe.core.source.DataSourceType;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -63,7 +65,7 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
|
||||||
.ref(DataSourceReference.id(getId())).maxRows(maxRows).build();
|
.ref(DataSourceReference.id(getId())).maxRows(maxRows).build();
|
||||||
con.performInputExchange(req, (QueryTableDataExchange.Response res, InputStream in) -> {
|
con.performInputExchange(req, (QueryTableDataExchange.Response res, InputStream in) -> {
|
||||||
var r = new TypedDataStreamParser(info.getDataType());
|
var r = new TypedDataStreamParser(info.getDataType());
|
||||||
r.parseStructures(in, TypedDataStructureNodeReader.immutable(info.getDataType()), nodes::add);
|
r.parseStructures(in, TypedDataStructureNodeReader.of(info.getDataType()), nodes::add);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return ArrayNode.of(nodes);
|
return ArrayNode.of(nodes);
|
||||||
|
@ -78,7 +80,7 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
|
||||||
private final TypedAbstractReader nodeReader;
|
private final TypedAbstractReader nodeReader;
|
||||||
|
|
||||||
{
|
{
|
||||||
nodeReader = TypedReusableDataStructureNodeReader.create(info.getDataType());
|
nodeReader = TypedDataStructureNodeReader.of(info.getDataType());
|
||||||
parser = new TypedDataStreamParser(info.getDataType());
|
parser = new TypedDataStreamParser(info.getDataType());
|
||||||
|
|
||||||
connection = XPipeConnection.open();
|
connection = XPipeConnection.open();
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class DataTableAccumulatorTest extends DaemonControl {
|
||||||
var acc = DataTableAccumulator.create(type);
|
var acc = DataTableAccumulator.create(type);
|
||||||
|
|
||||||
var val = type.convert(
|
var val = type.convert(
|
||||||
TupleNode.of(List.of(ValueNode.of("val1"), ValueNode.of("val2")))).orElseThrow();
|
TupleNode.of(List.of(ValueNode.ofValue("val1"), ValueNode.ofValue("val2")))).orElseThrow();
|
||||||
acc.add(val);
|
acc.add(val);
|
||||||
var table = acc.finish(":test");
|
var table = acc.finish(":test");
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import io.xpipe.core.data.node.ValueNode;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class GenericArrayReader implements GenericAbstractReader {
|
public class GenericArrayReader implements GenericAbstractReader {
|
||||||
|
|
||||||
|
@ -127,9 +128,9 @@ public class GenericArrayReader implements GenericAbstractReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onValue(byte[] value, boolean textual) {
|
public void onValue(byte[] value, Map<Integer, String> metaAttributes) {
|
||||||
if (currentReader != null) {
|
if (currentReader != null) {
|
||||||
currentReader.onValue(value, textual);
|
currentReader.onValue(value, metaAttributes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,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.mutable(value, textual));
|
put(ValueNode.of(value).tag(metaAttributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package io.xpipe.core.data.generic;
|
package io.xpipe.core.data.generic;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface GenericDataStreamCallback {
|
public interface GenericDataStreamCallback {
|
||||||
|
|
||||||
default void onName(String name) {
|
default void onName(String name) {
|
||||||
|
@ -17,6 +19,7 @@ public interface GenericDataStreamCallback {
|
||||||
default void onTupleEnd() {
|
default void onTupleEnd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
default void onValue(byte[] value, boolean textual) {
|
|
||||||
|
default void onValue(byte[] value, Map<Integer, String> metaAttributes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ public class GenericDataStreamParser {
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
parse(in, cb);
|
parse(in, cb);
|
||||||
}
|
}
|
||||||
|
var attributes = DataStructureNodeIO.parseAttributes(in);
|
||||||
cb.onTupleEnd();
|
cb.onTupleEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,19 +76,14 @@ public class GenericDataStreamParser {
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
parse(in, cb);
|
parse(in, cb);
|
||||||
}
|
}
|
||||||
|
var attributes = DataStructureNodeIO.parseAttributes(in);
|
||||||
cb.onArrayEnd();
|
cb.onArrayEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void parseValue(InputStream in, GenericDataStreamCallback cb) throws IOException {
|
private static void parseValue(InputStream in, GenericDataStreamCallback cb) throws IOException {
|
||||||
var type = in.read();
|
|
||||||
if (type == DataStructureNodeIO.VALUE_TYPE_NULL) {
|
|
||||||
cb.onValue(null, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var textual = type == DataStructureNodeIO.VALUE_TYPE_TEXT;
|
|
||||||
var size = in.read();
|
var size = in.read();
|
||||||
var data = in.readNBytes(size);
|
var data = in.readNBytes(size);
|
||||||
cb.onValue(data, textual);
|
var attributes = DataStructureNodeIO.parseAttributes(in);
|
||||||
|
cb.onValue(data, attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ public class GenericDataStreamWriter {
|
||||||
writeName(out, tuple.keyNameAt(i));
|
writeName(out, tuple.keyNameAt(i));
|
||||||
write(out, tuple.at(i));
|
write(out, tuple.at(i));
|
||||||
}
|
}
|
||||||
|
DataStructureNodeIO.writeAttributes(out, tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeArray(OutputStream out, ArrayNode array) throws IOException {
|
private static void writeArray(OutputStream out, ArrayNode array) throws IOException {
|
||||||
|
@ -49,17 +50,13 @@ public class GenericDataStreamWriter {
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
write(out, array.at(i));
|
write(out, array.at(i));
|
||||||
}
|
}
|
||||||
|
DataStructureNodeIO.writeAttributes(out, array);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeValue(OutputStream out, ValueNode n) throws IOException {
|
private static void writeValue(OutputStream out, ValueNode n) throws IOException {
|
||||||
out.write(DataStructureNodeIO.GENERIC_VALUE_ID);
|
out.write(DataStructureNodeIO.GENERIC_VALUE_ID);
|
||||||
if (n.isNull()) {
|
|
||||||
out.write(DataStructureNodeIO.VALUE_TYPE_NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.write(n.isTextual() ? DataStructureNodeIO.VALUE_TYPE_TEXT : DataStructureNodeIO.VALUE_TYPE_BARE);
|
|
||||||
out.write(n.getRawData().length);
|
out.write(n.getRawData().length);
|
||||||
out.write(n.getRawData());
|
out.write(n.getRawData());
|
||||||
|
DataStructureNodeIO.writeAttributes(out, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package io.xpipe.core.data.generic;
|
||||||
import io.xpipe.core.data.node.DataStructureNode;
|
import io.xpipe.core.data.node.DataStructureNode;
|
||||||
import io.xpipe.core.data.node.ValueNode;
|
import io.xpipe.core.data.node.ValueNode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class GenericDataStructureNodeReader implements GenericDataStreamCallback {
|
public class GenericDataStructureNodeReader implements GenericDataStreamCallback {
|
||||||
|
|
||||||
private DataStructureNode node;
|
private DataStructureNode node;
|
||||||
|
@ -78,12 +80,13 @@ public class GenericDataStructureNodeReader implements GenericDataStreamCallback
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onValue(byte[] value, boolean textual) {
|
public void onValue(byte[] value, Map<Integer, String> metaAttributes) {
|
||||||
if (hasReader()) {
|
if (hasReader()) {
|
||||||
reader.onValue(value, textual);
|
reader.onValue(value, metaAttributes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = ValueNode.mutable(value, textual);
|
|
||||||
|
node = ValueNode.of(value).tag(metaAttributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import io.xpipe.core.data.node.ValueNode;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class GenericTupleReader implements GenericAbstractReader {
|
public class GenericTupleReader implements GenericAbstractReader {
|
||||||
|
|
||||||
|
@ -139,9 +140,9 @@ public class GenericTupleReader implements GenericAbstractReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onValue(byte[] value, boolean textual) {
|
public void onValue(byte[] value, Map<Integer, String> metaAttributes) {
|
||||||
if (currentReader != null) {
|
if (currentReader != null) {
|
||||||
currentReader.onValue(value, textual);
|
currentReader.onValue(value, metaAttributes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +154,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.mutable(value, textual));
|
putNode(ValueNode.of(value).tag(metaAttributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,11 +17,7 @@ public abstract class ArrayNode extends DataStructureNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayNode of(List<DataStructureNode> nodes) {
|
public static ArrayNode of(List<DataStructureNode> nodes) {
|
||||||
return new SimpleArrayNode(true, nodes);
|
return new SimpleArrayNode(nodes);
|
||||||
}
|
|
||||||
|
|
||||||
public static ArrayNode of(boolean mutable, List<DataStructureNode> nodes) {
|
|
||||||
return new SimpleArrayNode(mutable, nodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ArrayNode() {
|
protected ArrayNode() {
|
||||||
|
@ -29,8 +25,12 @@ public abstract class ArrayNode extends DataStructureNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (!(o instanceof ArrayNode that)) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof ArrayNode that)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return getNodes().equals(that.getNodes());
|
return getNodes().equals(that.getNodes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,18 +49,10 @@ public abstract class ArrayNode extends DataStructureNode {
|
||||||
return "array node";
|
return "array node";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract ArrayNode immutableView();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract ArrayNode mutableCopy();
|
|
||||||
|
|
||||||
protected abstract String getIdentifier();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String toString(int indent) {
|
public final String toString(int indent) {
|
||||||
var content = getNodes().stream().map(n -> n.toString(indent)).collect(Collectors.joining(", "));
|
var content = getNodes().stream().map(n -> n.toString(indent)).collect(Collectors.joining(", "));
|
||||||
return "(" + getIdentifier() + ") [" + content + "]";
|
return "[" + content + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,20 +8,70 @@ import java.util.stream.Stream;
|
||||||
|
|
||||||
public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
||||||
|
|
||||||
public static final String KEY_TABLE_NAME = "tableName";
|
public static final Integer KEY_TABLE_NAME = 1;
|
||||||
public static final String KEY_ROW_NAME = "rowName";
|
public static final Integer KEY_ROW_NAME = 2;
|
||||||
|
public static final Integer BOOLEAN_TRUE = 3;
|
||||||
|
public static final Integer BOOLEAN_FALSE = 4;
|
||||||
|
public static final Integer INTEGER_VALUE = 5;
|
||||||
|
public static final Integer TEXT = 5;
|
||||||
|
|
||||||
private Properties properties = new Properties();
|
private Map<Integer, String> metaAttributes;
|
||||||
|
|
||||||
public String getMetaString(String key) {
|
public Map<Integer, String> getMetaAttributes() {
|
||||||
if (properties == null) {
|
return metaAttributes != null ? Collections.unmodifiableMap(metaAttributes) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataStructureNode tag(Integer key) {
|
||||||
|
if (metaAttributes == null) {
|
||||||
|
metaAttributes = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
metaAttributes.put(key, null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataStructureNode tag(Map<Integer, String> metaAttributes) {
|
||||||
|
if (metaAttributes == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.metaAttributes == null) {
|
||||||
|
this.metaAttributes = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.metaAttributes.putAll(metaAttributes);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataStructureNode tag(Integer key, String value) {
|
||||||
|
if (metaAttributes == null) {
|
||||||
|
metaAttributes = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
metaAttributes.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getMetaAttribute(Integer key) {
|
||||||
|
if (metaAttributes == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return properties.getProperty(key);
|
return metaAttributes.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract DataStructureNode mutableCopy();
|
public boolean hasMetaAttribute(Integer key) {
|
||||||
|
if (metaAttributes == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return metaAttributes.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataStructureNode mutable() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public String keyNameAt(int index) {
|
public String keyNameAt(int index) {
|
||||||
throw unsupported("key name at");
|
throw unsupported("key name at");
|
||||||
|
@ -50,8 +100,6 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
||||||
|
|
||||||
public abstract boolean isMutable();
|
public abstract boolean isMutable();
|
||||||
|
|
||||||
public abstract DataStructureNode immutableView();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return toString(0);
|
return toString(0);
|
||||||
|
@ -61,26 +109,6 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
||||||
throw unsupported("clear");
|
throw unsupported("clear");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTextual() {
|
|
||||||
throw unsupported("textual check");
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStructureNode setRaw(byte[] data) {
|
|
||||||
throw unsupported("set raw data");
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStructureNode set(Object newValue) {
|
|
||||||
throw unsupported("set");
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStructureNode set(Object newValue, boolean textual) {
|
|
||||||
throw unsupported("set");
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStructureNode set(int index, DataStructureNode node) {
|
|
||||||
throw unsupported("set at index");
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String toString(int indent);
|
public abstract String toString(int indent);
|
||||||
|
|
||||||
public boolean isTuple() {
|
public boolean isTuple() {
|
||||||
|
@ -99,6 +127,12 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DataStructureNode set(int index, DataStructureNode node) {
|
||||||
|
throw unsupported("set at index");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public final ValueNode asValue() {
|
public final ValueNode asValue() {
|
||||||
if (!isValue()) {
|
if (!isValue()) {
|
||||||
throw new UnsupportedOperationException(getName() + " is not a value node");
|
throw new UnsupportedOperationException(getName() + " is not a value node");
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
package io.xpipe.core.data.node;
|
package io.xpipe.core.data.node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class DataStructureNodeIO {
|
public class DataStructureNodeIO {
|
||||||
|
|
||||||
public static final int GENERIC_STRUCTURE_ID = 0;
|
public static final int GENERIC_STRUCTURE_ID = 0;
|
||||||
|
@ -13,7 +20,47 @@ public class DataStructureNodeIO {
|
||||||
public static final int TYPED_ARRAY_ID = 7;
|
public static final int TYPED_ARRAY_ID = 7;
|
||||||
public static final int TYPED_VALUE_ID = 8;
|
public static final int TYPED_VALUE_ID = 8;
|
||||||
|
|
||||||
public static final int VALUE_TYPE_BARE = 0;
|
public static void writeString(OutputStream out, String s) throws IOException {
|
||||||
public static final int VALUE_TYPE_TEXT = 1;
|
if (s != null) {
|
||||||
public static final int VALUE_TYPE_NULL = 2;
|
var b = s.getBytes(StandardCharsets.UTF_8);
|
||||||
|
out.write(b.length);
|
||||||
|
out.write(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String parseString(InputStream in) throws IOException {
|
||||||
|
var nameLength = in.read();
|
||||||
|
var name = new String(in.readNBytes(nameLength), StandardCharsets.UTF_8);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<Integer, String> parseAttributes(InputStream in) throws IOException {
|
||||||
|
var attributesLength = in.read();
|
||||||
|
if (attributesLength == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var map = new HashMap<Integer, String>();
|
||||||
|
for (int i = 0; i < attributesLength; i++) {
|
||||||
|
var key = in.read();
|
||||||
|
var value = parseString(in);
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeAttributes(OutputStream out, DataStructureNode s) throws IOException {
|
||||||
|
if (s.getMetaAttributes() != null) {
|
||||||
|
out.write(s.getMetaAttributes().size());
|
||||||
|
for (Map.Entry<Integer, String> entry : s.getMetaAttributes().entrySet()) {
|
||||||
|
Integer integer = entry.getKey();
|
||||||
|
var value = entry.getValue().getBytes(StandardCharsets.UTF_8);
|
||||||
|
out.write(integer);
|
||||||
|
out.write(value.length);
|
||||||
|
out.write(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.write(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
package io.xpipe.core.data.node;
|
|
||||||
|
|
||||||
public abstract class ImmutableValueNode extends ValueNode {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(int indent) {
|
|
||||||
return (isTextual() ? "\"" : "") + asString() + (isTextual() ? "\"" : "") + " (I)";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMutable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ValueNode immutableView() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataStructureNode setRaw(byte[] data) {
|
|
||||||
throw new UnsupportedOperationException("Value node is immutable");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataStructureNode set(Object newValue) {
|
|
||||||
throw new UnsupportedOperationException("Value node is immutable");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataStructureNode set(Object newValue, boolean textual) {
|
|
||||||
throw new UnsupportedOperationException("Value node is immutable");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -169,21 +169,11 @@ public class LinkedTupleNode extends TupleNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getIdentifier() {
|
public TupleNode mutable() {
|
||||||
return "linked tuple node";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TupleNode mutableCopy() {
|
|
||||||
if (isMutable()) {
|
if (isMutable()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LinkedTupleNode(tupleNodes.stream().map(n -> n.isMutable() ? n : n.mutableCopy()).toList());
|
return new LinkedTupleNode(tupleNodes.stream().map(n -> n.isMutable() ? n : n.mutable()).toList());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TupleNode immutableView() {
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
package io.xpipe.core.data.node;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
public class MutableValueNode extends ValueNode {
|
|
||||||
|
|
||||||
static final MutableValueNode NULL = new MutableValueNode(null, false);
|
|
||||||
|
|
||||||
private byte[] data;
|
|
||||||
private boolean textual;
|
|
||||||
|
|
||||||
MutableValueNode(byte[] data, boolean textual) {
|
|
||||||
this.data = data;
|
|
||||||
this.textual = textual;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String asString() {
|
|
||||||
return new String(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(int indent) {
|
|
||||||
if (isNull()) {
|
|
||||||
return "null (M)";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (textual ? "\"" : "") + new String(data) + (textual ? "\"" : "") + " (M)";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isNull() {
|
|
||||||
return data == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTextual() {
|
|
||||||
return textual;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMutable() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ValueNode immutableView() {
|
|
||||||
return new SimpleImmutableValueNode(data, textual);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ValueNode mutableCopy() {
|
|
||||||
return new MutableValueNode(data, textual);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataStructureNode setRaw(byte[] data) {
|
|
||||||
this.data = data;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataStructureNode set(Object newValue) {
|
|
||||||
if (newValue == null) {
|
|
||||||
this.data = null;
|
|
||||||
this.textual = false;
|
|
||||||
} else {
|
|
||||||
setRaw(newValue.toString().getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataStructureNode set(Object newValue, boolean textual) {
|
|
||||||
if (newValue == null && textual) {
|
|
||||||
throw new IllegalArgumentException("Can't set a textual null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newValue == null) {
|
|
||||||
this.data = null;
|
|
||||||
this.textual = false;
|
|
||||||
} else {
|
|
||||||
setRaw(newValue.toString().getBytes(StandardCharsets.UTF_8));
|
|
||||||
this.textual = textual;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getRawData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
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.stream.Collectors;
|
|
||||||
|
|
||||||
public class NoKeyTupleNode extends TupleNode {
|
|
||||||
|
|
||||||
private final boolean mutable;
|
|
||||||
private final List<DataStructureNode> 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(nodes.stream().map(DataStructureNode::determineDataType).toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getName() {
|
|
||||||
return "no key tuple node";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMutable() {
|
|
||||||
return mutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataStructureNode at(int index) {
|
|
||||||
return nodes.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return nodes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<KeyValue> getKeyValuePairs() {
|
|
||||||
return nodes.stream().map(n -> new KeyValue(null, n)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getKeyNames() {
|
|
||||||
return Collections.nCopies(size(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<DataStructureNode> getNodes() {
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getIdentifier() {
|
|
||||||
return "NK";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +1,29 @@
|
||||||
package io.xpipe.core.data.node;
|
package io.xpipe.core.data.node;
|
||||||
|
|
||||||
import java.util.*;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Spliterator;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
public class SimpleArrayNode extends ArrayNode {
|
public class SimpleArrayNode extends ArrayNode {
|
||||||
|
|
||||||
private final boolean mutable;
|
List<DataStructureNode> nodes;
|
||||||
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
|
@Override
|
||||||
public DataStructureNode put(DataStructureNode node) {
|
public DataStructureNode put(DataStructureNode node) {
|
||||||
checkMutable();
|
|
||||||
|
|
||||||
nodes.add(node);
|
nodes.add(node);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataStructureNode set(int index, DataStructureNode node) {
|
public DataStructureNode set(int index, DataStructureNode node) {
|
||||||
checkMutable();
|
|
||||||
|
|
||||||
nodes.add(index, node);
|
nodes.add(index, node);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -47,34 +38,13 @@ public class SimpleArrayNode extends ArrayNode {
|
||||||
return nodes.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
|
@Override
|
||||||
public boolean isMutable() {
|
public boolean isMutable() {
|
||||||
return mutable;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ArrayNode immutableView() {
|
|
||||||
return new SimpleArrayNode(false, nodes.stream()
|
|
||||||
.map(DataStructureNode::immutableView)
|
|
||||||
.collect(Collectors.toCollection(ArrayList::new)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataStructureNode clear() {
|
public DataStructureNode clear() {
|
||||||
checkMutable();
|
|
||||||
|
|
||||||
nodes.clear();
|
nodes.clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -101,13 +71,11 @@ public class SimpleArrayNode extends ArrayNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DataStructureNode> getNodes() {
|
public List<DataStructureNode> getNodes() {
|
||||||
return nodes;
|
return Collections.unmodifiableList(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataStructureNode remove(int index) {
|
public DataStructureNode remove(int index) {
|
||||||
checkMutable();
|
|
||||||
|
|
||||||
nodes.remove(index);
|
nodes.remove(index);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,15 @@
|
||||||
package io.xpipe.core.data.node;
|
package io.xpipe.core.data.node;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public class SimpleImmutableValueNode extends ImmutableValueNode {
|
public class SimpleImmutableValueNode extends ValueNode {
|
||||||
|
|
||||||
private final byte[] data;
|
private final byte @NonNull [] data;
|
||||||
private final boolean textual;
|
|
||||||
|
|
||||||
SimpleImmutableValueNode(byte[] data, boolean textual) {
|
SimpleImmutableValueNode(byte @NonNull [] data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.textual = textual;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ValueNode mutableCopy() {
|
|
||||||
return ValueNode.mutable(data, textual);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isTextual() {
|
|
||||||
return textual;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRawData() {
|
public byte[] getRawData() {
|
||||||
|
@ -28,10 +18,20 @@ public class SimpleImmutableValueNode extends ImmutableValueNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final String asString() {
|
public final String asString() {
|
||||||
if (getRawData() == null) {
|
if (getRawData().length == 0 && !hasMetaAttribute(TEXT)) {
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String(getRawData(), StandardCharsets.UTF_8);
|
return new String(getRawData(), StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(int indent) {
|
||||||
|
return (hasMetaAttribute(TEXT) ? "\"" : "") + asString() + (hasMetaAttribute(TEXT) ? "\"" : "") + " (I)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMutable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,41 +3,23 @@ package io.xpipe.core.data.node;
|
||||||
import io.xpipe.core.data.type.DataType;
|
import io.xpipe.core.data.type.DataType;
|
||||||
import io.xpipe.core.data.type.TupleType;
|
import io.xpipe.core.data.type.TupleType;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
import java.util.stream.Collectors;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
||||||
public class SimpleTupleNode extends TupleNode {
|
public class SimpleTupleNode extends TupleNode {
|
||||||
|
|
||||||
private final boolean mutable;
|
|
||||||
private final List<String> names;
|
private final List<String> names;
|
||||||
private final List<DataStructureNode> nodes;
|
private final List<DataStructureNode> nodes;
|
||||||
|
|
||||||
SimpleTupleNode(boolean mutable, List<String> names, List<DataStructureNode> nodes) {
|
public SimpleTupleNode(List<String> names, List<DataStructureNode> nodes) {
|
||||||
this.mutable = mutable;
|
this.names = names;
|
||||||
this.names = mutable ? names : Collections.unmodifiableList(names);
|
this.nodes = nodes;
|
||||||
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
|
@Override
|
||||||
public DataStructureNode set(int index, DataStructureNode node) {
|
public DataStructureNode set(int index, DataStructureNode node) {
|
||||||
checkMutable();
|
|
||||||
|
|
||||||
nodes.set(index, node);
|
nodes.set(index, node);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +36,7 @@ public class SimpleTupleNode extends TupleNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isMutable() {
|
public boolean isMutable() {
|
||||||
return mutable;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -83,8 +65,6 @@ public class SimpleTupleNode extends TupleNode {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DataStructureNode clear() {
|
public DataStructureNode clear() {
|
||||||
checkMutable();
|
|
||||||
|
|
||||||
nodes.clear();
|
nodes.clear();
|
||||||
names.clear();
|
names.clear();
|
||||||
return this;
|
return this;
|
||||||
|
@ -118,7 +98,7 @@ public class SimpleTupleNode extends TupleNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getIdentifier() {
|
public TupleNode mutable() {
|
||||||
return "S";
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ public abstract class TupleNode extends DataStructureNode {
|
||||||
throw new IllegalArgumentException("Nodes must be not null");
|
throw new IllegalArgumentException("Nodes must be not null");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NoKeyTupleNode(true, nodes);
|
return new SimpleTupleNode(null, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TupleNode of(List<String> names, List<DataStructureNode> nodes) {
|
public static TupleNode of(List<String> names, List<DataStructureNode> nodes) {
|
||||||
|
@ -30,41 +30,20 @@ public abstract class TupleNode extends DataStructureNode {
|
||||||
throw new IllegalArgumentException("Names and nodes must have the same length");
|
throw new IllegalArgumentException("Names and nodes must have the same length");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SimpleTupleNode(true, names, nodes);
|
return new SimpleTupleNode(names, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TupleNode of(boolean mutable, List<String> names, List<DataStructureNode> nodes) {
|
@Override
|
||||||
if (names == null) {
|
public abstract TupleNode mutable();
|
||||||
throw new IllegalArgumentException("Names must be not null");
|
|
||||||
}
|
|
||||||
if (nodes == null) {
|
|
||||||
throw new IllegalArgumentException("Nodes must be not null");
|
|
||||||
}
|
|
||||||
return new SimpleTupleNode(mutable, names, nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean isTuple() {
|
public final boolean isTuple() {
|
||||||
return true;
|
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
|
@Override
|
||||||
public String toString(int indent) {
|
public String toString(int indent) {
|
||||||
var is = " ".repeat(indent);
|
var is = " ".repeat(indent);
|
||||||
var start = "(" + getIdentifier() + ") {\n";
|
var start = "{\n";
|
||||||
var kvs = getKeyValuePairs().stream().map(kv -> {
|
var kvs = getKeyValuePairs().stream().map(kv -> {
|
||||||
if (kv.key() == null) {
|
if (kv.key() == null) {
|
||||||
return is + " " + kv.value().toString(indent + 1) + "\n";
|
return is + " " + kv.value().toString(indent + 1) + "\n";
|
||||||
|
|
|
@ -5,17 +5,23 @@ import io.xpipe.core.data.type.ValueType;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public abstract class ValueNode extends DataStructureNode {
|
public abstract class ValueNode extends DataStructureNode {
|
||||||
|
|
||||||
|
|
||||||
protected ValueNode() {
|
protected ValueNode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (!(o instanceof ValueNode that)) return false;
|
return true;
|
||||||
return Arrays.equals(getRawData(), that.getRawData());
|
}
|
||||||
|
if (!(o instanceof ValueNode that)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Arrays.equals(getRawData(), that.getRawData()) && Objects.equals(getMetaAttributes(), that.getMetaAttributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,68 +29,18 @@ public abstract class ValueNode extends DataStructureNode {
|
||||||
return Arrays.hashCode(getRawData());
|
return Arrays.hashCode(getRawData());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static ValueNode nullValue() {
|
||||||
public abstract ValueNode immutableView();
|
return new SimpleImmutableValueNode(new byte[0]);
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract ValueNode mutableCopy();
|
|
||||||
|
|
||||||
public static ValueNode immutable(byte[] data, boolean textual) {
|
|
||||||
return new SimpleImmutableValueNode(data, textual);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueNode immutable(Object o, boolean textual) {
|
|
||||||
if (o == null) {
|
|
||||||
return immutableNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
return immutable(o.toString().getBytes(StandardCharsets.UTF_8), textual);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueNode immutableNull() {
|
|
||||||
return MutableValueNode.NULL.immutableView();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueNode mutableNull() {
|
|
||||||
return MutableValueNode.NULL.mutableCopy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueNode mutable(byte[] data, boolean textual) {
|
|
||||||
return new MutableValueNode(data, textual);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueNode mutable(Object o, boolean textual) {
|
|
||||||
return mutable(o.toString().getBytes(StandardCharsets.UTF_8), textual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueNode of(byte[] data) {
|
public static ValueNode of(byte[] data) {
|
||||||
return mutable(data, false);
|
return new SimpleImmutableValueNode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueNode of(Object o) {
|
public static ValueNode of(Object o) {
|
||||||
return mutable(o, false);
|
return of(o.toString().getBytes(StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueNode ofText(byte[] data) {
|
|
||||||
return mutable(data, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueNode ofText(Object o) {
|
|
||||||
return mutable(o, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract boolean isTextual();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract DataStructureNode setRaw(byte[] data);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract DataStructureNode set(Object newValue);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract DataStructureNode set(Object newValue, boolean textual);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int asInt() {
|
public final int asInt() {
|
||||||
return Integer.parseInt(asString());
|
return Integer.parseInt(asString());
|
||||||
|
@ -107,9 +63,4 @@ public abstract class ValueNode extends DataStructureNode {
|
||||||
|
|
||||||
public abstract byte[] getRawData();
|
public abstract byte[] getRawData();
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isNull() {
|
|
||||||
return getRawData() == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import io.xpipe.core.data.type.TupleType;
|
||||||
|
|
||||||
public interface TypedDataStreamCallback {
|
public interface TypedDataStreamCallback {
|
||||||
|
|
||||||
default void onValue(byte[] data, boolean textual) {
|
default void onValue(byte[] data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default void onGenericNode(DataStructureNode node) {
|
default void onGenericNode(DataStructureNode node) {
|
||||||
|
|
|
@ -129,15 +129,8 @@ public class TypedDataStreamParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseValue(InputStream in, TypedDataStreamCallback cb) throws IOException {
|
private void parseValue(InputStream in, TypedDataStreamCallback cb) throws IOException {
|
||||||
var type = in.read();
|
|
||||||
if (type == DataStructureNodeIO.VALUE_TYPE_NULL) {
|
|
||||||
cb.onValue(null, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var textual = type == DataStructureNodeIO.VALUE_TYPE_TEXT;
|
|
||||||
var size = in.read();
|
var size = in.read();
|
||||||
var data = in.readNBytes(size);
|
var data = in.readNBytes(size);
|
||||||
cb.onValue(data, textual);
|
cb.onValue(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,6 @@ public class TypedDataStreamWriter {
|
||||||
|
|
||||||
private static void writeValue(OutputStream out, ValueNode n) throws IOException {
|
private static void writeValue(OutputStream out, ValueNode n) throws IOException {
|
||||||
out.write(DataStructureNodeIO.TYPED_VALUE_ID);
|
out.write(DataStructureNodeIO.TYPED_VALUE_ID);
|
||||||
if (n.isNull()) {
|
|
||||||
out.write(DataStructureNodeIO.VALUE_TYPE_NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.write(n.isTextual() ? DataStructureNodeIO.VALUE_TYPE_TEXT : DataStructureNodeIO.VALUE_TYPE_BARE);
|
|
||||||
out.write(n.getRawData().length);
|
out.write(n.getRawData().length);
|
||||||
out.write(n.getRawData());
|
out.write(n.getRawData());
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,15 @@ import io.xpipe.core.data.type.DataTypeVisitors;
|
||||||
import io.xpipe.core.data.type.TupleType;
|
import io.xpipe.core.data.type.TupleType;
|
||||||
|
|
||||||
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) {
|
public static TypedDataStructureNodeReader of(DataType type) {
|
||||||
return new TypedDataStructureNodeReader(type, false);
|
return new TypedDataStructureNodeReader(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TypedDataStructureNodeReader immutable(DataType type) {
|
|
||||||
return new TypedDataStructureNodeReader(type, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final boolean makeImmutable;
|
|
||||||
private DataStructureNode readNode;
|
private DataStructureNode readNode;
|
||||||
|
|
||||||
private final Stack<List<DataStructureNode>> children;
|
private final Stack<List<DataStructureNode>> children;
|
||||||
|
@ -31,12 +25,11 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
||||||
private DataType expectedType;
|
private DataType expectedType;
|
||||||
private int currentExpectedTypeIndex;
|
private int currentExpectedTypeIndex;
|
||||||
|
|
||||||
private TypedDataStructureNodeReader(DataType type, boolean makeImmutable) {
|
private TypedDataStructureNodeReader(DataType type) {
|
||||||
flattened = new ArrayList<>();
|
flattened = new ArrayList<>();
|
||||||
type.visit(DataTypeVisitors.flatten(flattened::add));
|
type.visit(DataTypeVisitors.flatten(flattened::add));
|
||||||
children = new Stack<>();
|
children = new Stack<>();
|
||||||
nodes = new Stack<>();
|
nodes = new Stack<>();
|
||||||
this.makeImmutable = makeImmutable;
|
|
||||||
expectedType = flattened.get(0);
|
expectedType = flattened.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,12 +74,12 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onValue(byte[] data, boolean textual) {
|
public void onValue(byte[] data) {
|
||||||
if (!expectedType.isValue()) {
|
if (!expectedType.isValue()) {
|
||||||
throw new IllegalStateException("Expected " + expectedType.getName() + " but got value");
|
throw new IllegalStateException("Expected " + expectedType.getName() + " but got value");
|
||||||
}
|
}
|
||||||
|
|
||||||
var val = makeImmutable ? ValueNode.immutable(data, textual) : ValueNode.mutable(data, textual);
|
var val = ValueNode.of(data);
|
||||||
finishNode(val);
|
finishNode(val);
|
||||||
moveExpectedType(false);
|
moveExpectedType(false);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +94,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
||||||
throw new IllegalStateException("Expected " + expectedType.getName() + " but got generic node");
|
throw new IllegalStateException("Expected " + expectedType.getName() + " but got generic node");
|
||||||
}
|
}
|
||||||
|
|
||||||
finishNode(makeImmutable ? node.immutableView() : node);
|
finishNode(node);
|
||||||
moveExpectedType(false);
|
moveExpectedType(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,10 +110,7 @@ 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 tupleNames = makeImmutable ?
|
var newNode = new SimpleTupleNode(tupleType.getNames(), l);
|
||||||
Collections.unmodifiableList(tupleType.getNames()) : new ArrayList<>(tupleType.getNames());
|
|
||||||
var tupleNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
|
|
||||||
var newNode = TupleNode.of(!makeImmutable, tupleNames, tupleNodes);
|
|
||||||
nodes.push(newNode);
|
nodes.push(newNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,8 +149,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
||||||
var l = new ArrayList<DataStructureNode>();
|
var l = new ArrayList<DataStructureNode>();
|
||||||
children.push(l);
|
children.push(l);
|
||||||
|
|
||||||
var arrayNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
|
var newNode = ArrayNode.of(l);
|
||||||
var newNode = ArrayNode.of(arrayNodes);
|
|
||||||
nodes.push(newNode);
|
nodes.push(newNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
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.DataTypeVisitors;
|
|
||||||
import io.xpipe.core.data.type.TupleType;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
public class TypedReusableDataStructureNodeReader implements TypedAbstractReader {
|
|
||||||
|
|
||||||
public static TypedReusableDataStructureNodeReader create(DataType type) {
|
|
||||||
return new TypedReusableDataStructureNodeReader(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<DataType> flattened;
|
|
||||||
private final TypedDataStructureNodeReader initialReader;
|
|
||||||
private DataStructureNode node;
|
|
||||||
private final Stack<Integer> indices;
|
|
||||||
private int arrayDepth;
|
|
||||||
|
|
||||||
private TypedReusableDataStructureNodeReader(DataType type) {
|
|
||||||
flattened = new ArrayList<>();
|
|
||||||
indices = new Stack<>();
|
|
||||||
initialReader = TypedDataStructureNodeReader.mutable(type);
|
|
||||||
type.visit(DataTypeVisitors.flatten(d -> flattened.add(d)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDone() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataStructureNode create() {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isInArray() {
|
|
||||||
return arrayDepth >= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean initialized() {
|
|
||||||
return node != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onValue(byte[] data, boolean textual) {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onValue(data, textual);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInArray()) {
|
|
||||||
getCurrentParent().set(indices.peek(), ValueNode.mutable(data, textual));
|
|
||||||
} else {
|
|
||||||
getCurrent().setRaw(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!indices.isEmpty()) {
|
|
||||||
indices.push(indices.pop() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGenericNode(DataStructureNode node) {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onGenericNode(node);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataStructureNode getCurrent() {
|
|
||||||
var current = node;
|
|
||||||
for (var index : indices) {
|
|
||||||
current = current.at(index);
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTupleBegin(TupleType type) {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onTupleBegin(type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
indices.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTupleEnd() {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onTupleEnd();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
indices.pop();
|
|
||||||
if (!indices.isEmpty()) {
|
|
||||||
indices.push(indices.pop() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onArrayBegin(int size) {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onArrayBegin(size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrent().clear();
|
|
||||||
indices.push(0);
|
|
||||||
arrayDepth++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onArrayEnd() {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onArrayEnd();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
indices.pop();
|
|
||||||
arrayDepth--;
|
|
||||||
if (!indices.isEmpty()) {
|
|
||||||
indices.push(indices.pop() + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNodeBegin() {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onNodeBegin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNodeEnd() {
|
|
||||||
if (!initialized()) {
|
|
||||||
initialReader.onNodeEnd();
|
|
||||||
node = initialReader.create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,6 +16,9 @@ import java.util.Optional;
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||||
public interface DataStore {
|
public interface DataStore {
|
||||||
|
|
||||||
|
default DataStoreFlow getFlow() {
|
||||||
|
return DataStoreFlow.INOUT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether this store can be opened.
|
* Checks whether this store can be opened.
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.xpipe.core.store;
|
||||||
|
|
||||||
|
public enum DataStoreFlow {
|
||||||
|
|
||||||
|
INPUT,
|
||||||
|
OUTPUT,
|
||||||
|
INOUT
|
||||||
|
}
|
|
@ -124,7 +124,8 @@ public class ShellTypes {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Charset getCharset() {
|
public Charset getCharset() {
|
||||||
return StandardCharsets.UTF_16LE;
|
// TODO
|
||||||
|
return StandardCharsets.ISO_8859_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
package io.xpipe.core.test;
|
package io.xpipe.core.test;
|
||||||
|
|
||||||
import io.xpipe.core.data.node.DataStructureNode;
|
|
||||||
import io.xpipe.core.data.generic.GenericDataStreamParser;
|
import io.xpipe.core.data.generic.GenericDataStreamParser;
|
||||||
import io.xpipe.core.data.generic.GenericDataStreamWriter;
|
import io.xpipe.core.data.generic.GenericDataStreamWriter;
|
||||||
import io.xpipe.core.data.generic.GenericDataStructureNodeReader;
|
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.DataStructureNode;
|
||||||
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.TypedDataStreamParser;
|
||||||
import io.xpipe.core.data.typed.TypedDataStreamWriter;
|
import io.xpipe.core.data.typed.TypedDataStreamWriter;
|
||||||
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
|
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.ParameterizedTest;
|
||||||
|
@ -19,7 +18,6 @@ import org.junit.jupiter.params.provider.EnumSource;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DataStructureTest {
|
public class DataStructureTest {
|
||||||
|
@ -91,74 +89,20 @@ public class DataStructureTest {
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@EnumSource(DataStructureTests.TypedDataset.class)
|
@EnumSource(DataStructureTests.TypedDataset.class)
|
||||||
public void testMutableTypedIo(DataStructureTests.TypedDataset ds) throws IOException {
|
public void testTypedIo(DataStructureTests.TypedDataset ds) throws IOException {
|
||||||
for (var node : ds.nodes) {
|
for (var node : ds.nodes) {
|
||||||
var dataOut = new ByteArrayOutputStream();
|
var dataOut = new ByteArrayOutputStream();
|
||||||
TypedDataStreamWriter.writeStructure(dataOut, node, ds.type);
|
TypedDataStreamWriter.writeStructure(dataOut, node, ds.type);
|
||||||
var data = dataOut.toByteArray();
|
var data = dataOut.toByteArray();
|
||||||
|
|
||||||
var reader = TypedDataStructureNodeReader.mutable(ds.type);
|
var reader = TypedDataStructureNodeReader.of(ds.type);
|
||||||
new TypedDataStreamParser(ds.type).parseStructure(new ByteArrayInputStream(data), reader);
|
new TypedDataStreamParser(ds.type).parseStructure(new ByteArrayInputStream(data), reader);
|
||||||
var readNode = reader.create();
|
var readNode = reader.create();
|
||||||
|
|
||||||
Assertions.assertEquals(node, readNode);
|
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).parseStructure(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.setRaw("abc".getBytes(StandardCharsets.UTF_8));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (readNode.isTuple() || readNode.isArray()) {
|
if (readNode.isTuple() || readNode.isArray()) {
|
||||||
Assertions.assertEquals(readNode.size(), node.size());
|
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 = TypedReusableDataStructureNodeReader.create(ds.type);
|
|
||||||
|
|
||||||
for (var node : ds.nodes) {
|
|
||||||
new TypedDataStreamParser(ds.type).parseStructure(in, reader);
|
|
||||||
var readNode = reader.create();
|
|
||||||
Assertions.assertEquals(node, readNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class DataStructureTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DataStructureNode createTestData12() {
|
private static DataStructureNode createTestData12() {
|
||||||
var val = ValueNode.of(null);
|
var val = ValueNode.nullValue();
|
||||||
var flatArray = ArrayNode.of();
|
var flatArray = ArrayNode.of();
|
||||||
var flatTuple = TupleNode.builder().add("key1", val).build();
|
var flatTuple = TupleNode.builder().add("key1", val).build();
|
||||||
var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
|
var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
|
||||||
|
@ -101,7 +101,7 @@ public class DataStructureTests {
|
||||||
|
|
||||||
public static DataStructureNode createTestData32() {
|
public static DataStructureNode createTestData32() {
|
||||||
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
|
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
|
||||||
var flatTuple = TupleNode.builder().add("key1", ValueNode.of(null)).add("key2", ValueNode.of(null)).build();
|
var flatTuple = TupleNode.builder().add("key1", ValueNode.nullValue()).add("key2", ValueNode.nullValue()).build();
|
||||||
var flatArray = ArrayNode.of(List.of(val, flatTuple));
|
var flatArray = ArrayNode.of(List.of(val, flatTuple));
|
||||||
return flatArray;
|
return flatArray;
|
||||||
}
|
}
|
||||||
|
@ -112,13 +112,13 @@ public class DataStructureTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DataStructureNode createTestData42() {
|
public static DataStructureNode createTestData42() {
|
||||||
var val = ValueNode.of(null);
|
var val = ValueNode.nullValue();
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DataStructureNode createTestData51() {
|
public static DataStructureNode createTestData51() {
|
||||||
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
|
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
|
||||||
var flatArray = ArrayNode.of(List.of(val, ValueNode.of(null)));
|
var flatArray = ArrayNode.of(List.of(val, ValueNode.nullValue()));
|
||||||
var array1 = ArrayNode.of(List.of(flatArray));
|
var array1 = ArrayNode.of(List.of(flatArray));
|
||||||
var array2 = ArrayNode.of(List.of(array1, array1));
|
var array2 = ArrayNode.of(List.of(array1, array1));
|
||||||
return array2;
|
return array2;
|
||||||
|
@ -143,7 +143,7 @@ public class DataStructureTests {
|
||||||
|
|
||||||
public static DataStructureNode createTestData61() {
|
public static DataStructureNode createTestData61() {
|
||||||
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
|
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
|
||||||
var array = ArrayNode.of(List.of(val, ValueNode.of(null)));
|
var array = ArrayNode.of(List.of(val, ValueNode.nullValue()));
|
||||||
var tuple = TupleNode.builder()
|
var tuple = TupleNode.builder()
|
||||||
.add(val).add("key2", array).build();
|
.add(val).add("key2", array).build();
|
||||||
return tuple;
|
return tuple;
|
||||||
|
@ -176,7 +176,7 @@ public class DataStructureTests {
|
||||||
|
|
||||||
public static DataStructureNode createTestData73() {
|
public static DataStructureNode createTestData73() {
|
||||||
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
|
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
|
||||||
var array = ArrayNode.of(List.of(val, ValueNode.of(null)));
|
var array = ArrayNode.of(List.of(val, ValueNode.nullValue()));
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
package io.xpipe.extension;
|
package io.xpipe.extension;
|
||||||
|
|
||||||
import io.xpipe.core.dialog.Dialog;
|
import io.xpipe.core.dialog.Dialog;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.*;
|
||||||
import io.xpipe.core.store.MachineFileStore;
|
|
||||||
import io.xpipe.core.store.ShellStore;
|
|
||||||
import io.xpipe.core.store.StreamDataStore;
|
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -91,4 +88,8 @@ public interface DataStoreProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Class<?>> getStoreClasses();
|
List<Class<?>> getStoreClasses();
|
||||||
|
|
||||||
|
default DataStoreFlow[] getPossibleFlows() {
|
||||||
|
return new DataStoreFlow[]{DataStoreFlow.INPUT};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ import io.xpipe.core.charsetter.NewLine;
|
||||||
import io.xpipe.core.charsetter.StreamCharset;
|
import io.xpipe.core.charsetter.StreamCharset;
|
||||||
import io.xpipe.core.dialog.Dialog;
|
import io.xpipe.core.dialog.Dialog;
|
||||||
import io.xpipe.core.dialog.QueryConverter;
|
import io.xpipe.core.dialog.QueryConverter;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.source.DataSource;
|
||||||
import io.xpipe.core.store.LocalStore;
|
import io.xpipe.core.store.*;
|
||||||
import io.xpipe.core.store.MachineFileStore;
|
|
||||||
import io.xpipe.core.store.ShellStore;
|
|
||||||
import io.xpipe.core.util.Secret;
|
import io.xpipe.core.util.Secret;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class DialogHelper {
|
public class DialogHelper {
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
|
@ -50,6 +50,10 @@ public class DialogHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dialog dataStoreFlowQuery(DataStoreFlow flow, DataStoreFlow[] available) {
|
||||||
|
return Dialog.choice("flow", o -> o.toString(), true, flow, available);
|
||||||
|
}
|
||||||
|
|
||||||
public static Dialog shellQuery(DataStore store) {
|
public static Dialog shellQuery(DataStore store) {
|
||||||
var storeName = XPipeDaemon.getInstance()
|
var storeName = XPipeDaemon.getInstance()
|
||||||
.getStoreName(store)
|
.getStoreName(store)
|
||||||
|
@ -111,6 +115,22 @@ public class DialogHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dialog sourceQuery(DataSource<?> source, Predicate<DataSource<?>> filter) {
|
||||||
|
var id = XPipeDaemon.getInstance()
|
||||||
|
.getSourceId(source)
|
||||||
|
.orElse(null);
|
||||||
|
return Dialog.query("Source Id", false, true, false, id, QueryConverter.STRING)
|
||||||
|
.map((String newName) -> {
|
||||||
|
var found = XPipeDaemon.getInstance()
|
||||||
|
.getSource(newName)
|
||||||
|
.orElseThrow();
|
||||||
|
if (!filter.test(found)) {
|
||||||
|
throw new IllegalArgumentException("Incompatible store type");
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static Dialog passwordQuery(Secret password) {
|
public static Dialog passwordQuery(Secret password) {
|
||||||
return Dialog.querySecret("Password", false, true, password);
|
return Dialog.querySecret("Password", false, true, password);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,5 +53,9 @@ public interface SupportedApplicationProvider {
|
||||||
default String getGraphicIcon() {
|
default String getGraphicIcon() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean isApplicable(DataSource<?> source) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package io.xpipe.extension;
|
package io.xpipe.extension;
|
||||||
|
|
||||||
|
import io.xpipe.core.source.DataSource;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
import io.xpipe.fxcomps.Comp;
|
import io.xpipe.fxcomps.Comp;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
|
@ -25,9 +26,15 @@ public interface XPipeDaemon {
|
||||||
|
|
||||||
Comp<?> namedStoreChooser(ObservableValue<Predicate<DataStore>> filter, Property<? extends DataStore> selected, DataStoreProvider.Category category);
|
Comp<?> namedStoreChooser(ObservableValue<Predicate<DataStore>> filter, Property<? extends DataStore> selected, DataStoreProvider.Category category);
|
||||||
|
|
||||||
|
Comp<?> namedSourceChooser(ObservableValue<Predicate<DataSource<?>>> filter, Property<? extends DataSource<?>> selected, DataSourceProvider.Category category);
|
||||||
|
|
||||||
Comp<?> sourceProviderChooser(Property<DataSourceProvider<?>> provider, DataSourceProvider.Category category);
|
Comp<?> sourceProviderChooser(Property<DataSourceProvider<?>> provider, DataSourceProvider.Category category);
|
||||||
|
|
||||||
Optional<DataStore> getNamedStore(String name);
|
Optional<DataStore> getNamedStore(String name);
|
||||||
|
|
||||||
|
Optional<DataSource<?>> getSource(String id);
|
||||||
|
|
||||||
Optional<String> getStoreName(DataStore store);
|
Optional<String> getStoreName(DataStore store);
|
||||||
|
|
||||||
|
Optional<String> getSourceId(DataSource<?> source);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package io.xpipe.extension.comp;
|
||||||
|
|
||||||
|
import io.xpipe.core.store.DataStoreFlow;
|
||||||
|
import io.xpipe.extension.I18n;
|
||||||
|
import io.xpipe.fxcomps.SimpleComp;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class DataStoreFlowChoiceComp extends SimpleComp {
|
||||||
|
|
||||||
|
Property<DataStoreFlow> selected;
|
||||||
|
DataStoreFlow[] available;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Region createSimple() {
|
||||||
|
var map = new LinkedHashMap<DataStoreFlow, ObservableValue<String>>();
|
||||||
|
map.put(DataStoreFlow.INPUT, I18n.observable("extension.input"));
|
||||||
|
map.put(DataStoreFlow.OUTPUT, I18n.observable("extension.output"));
|
||||||
|
map.put(DataStoreFlow.INOUT, I18n.observable("extension.inout"));
|
||||||
|
return new ToggleGroupComp<>(selected, map).apply(struc -> {
|
||||||
|
new FancyTooltipAugment<>("extension.inputDescription").augment(struc.get().getChildren().get(0));
|
||||||
|
new FancyTooltipAugment<>("extension.outputDescription").augment(struc.get().getChildren().get(1));
|
||||||
|
new FancyTooltipAugment<>("extension.inoutDescription").augment(struc.get().getChildren().get(2));
|
||||||
|
}).createRegion();
|
||||||
|
}
|
||||||
|
}
|
|
@ -149,6 +149,10 @@ public class DynamicOptionsBuilder<T> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DynamicOptionsBuilder<T> addComp(String nameKey, Comp<?> comp, Property<?> prop) {
|
||||||
|
return addComp(I18n.observable(nameKey), comp, prop);
|
||||||
|
}
|
||||||
|
|
||||||
public DynamicOptionsBuilder<T> addComp(ObservableValue<String> name, Comp<?> comp, Property<?> prop) {
|
public DynamicOptionsBuilder<T> addComp(ObservableValue<String> name, Comp<?> comp, Property<?> prop) {
|
||||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||||
props.add(prop);
|
props.add(prop);
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package io.xpipe.extension.comp;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXTooltip;
|
||||||
|
import io.xpipe.extension.I18n;
|
||||||
|
import io.xpipe.fxcomps.CompStructure;
|
||||||
|
import io.xpipe.fxcomps.Shortcuts;
|
||||||
|
import io.xpipe.fxcomps.augment.Augment;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.stage.Window;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class FancyTooltipAugment<S extends CompStructure<?>> implements Augment<S> {
|
||||||
|
|
||||||
|
static {
|
||||||
|
JFXTooltip.setHoverDelay(Duration.millis(400));
|
||||||
|
JFXTooltip.setVisibleDuration(Duration.INDEFINITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ObservableValue<String> text;
|
||||||
|
|
||||||
|
public FancyTooltipAugment(Supplier<String> text) {
|
||||||
|
this.text = new SimpleObjectProperty<>(text.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public FancyTooltipAugment(String key) {
|
||||||
|
this.text = I18n.observable(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void augment(S struc) {
|
||||||
|
var tt = new FocusTooltip();
|
||||||
|
var toDisplay = text.getValue();
|
||||||
|
if (Shortcuts.getShortcut(struc.get()) != null) {
|
||||||
|
toDisplay = toDisplay + " (" + Shortcuts.getShortcut(struc.get()).getDisplayText() + ")";
|
||||||
|
}
|
||||||
|
tt.textProperty().setValue(toDisplay);
|
||||||
|
tt.setStyle("-fx-font-size: 11pt;");
|
||||||
|
JFXTooltip.install(struc.get(), tt);
|
||||||
|
tt.setWrapText(true);
|
||||||
|
tt.setMaxWidth(400);
|
||||||
|
tt.getStyleClass().add("fancy-tooltip");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void augment(Node region) {
|
||||||
|
var tt = new FocusTooltip();
|
||||||
|
var toDisplay = text.getValue();
|
||||||
|
if (Shortcuts.getShortcut((Region) region) != null) {
|
||||||
|
toDisplay = toDisplay + " (" + Shortcuts.getShortcut((Region) region).getDisplayText() + ")";
|
||||||
|
}
|
||||||
|
tt.textProperty().setValue(toDisplay);
|
||||||
|
tt.setStyle("-fx-font-size: 11pt;");
|
||||||
|
JFXTooltip.install(region, tt);
|
||||||
|
tt.setWrapText(true);
|
||||||
|
tt.setMaxWidth(400);
|
||||||
|
tt.getStyleClass().add("fancy-tooltip");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FocusTooltip extends JFXTooltip {
|
||||||
|
|
||||||
|
public FocusTooltip() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public FocusTooltip(String string) {
|
||||||
|
super(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void show() {
|
||||||
|
Window owner = getOwnerWindow();
|
||||||
|
if (owner.isFocused())
|
||||||
|
super.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,3 +10,9 @@ hostFeatureUnsupported=Host does not support the feature $FEATURE$
|
||||||
namedHostFeatureUnsupported=$HOST$ does not support this feature
|
namedHostFeatureUnsupported=$HOST$ does not support this feature
|
||||||
namedHostNotActive=$HOST$ is not active
|
namedHostNotActive=$HOST$ is not active
|
||||||
noInformationAvailable=No information available
|
noInformationAvailable=No information available
|
||||||
|
input=Input
|
||||||
|
output=Output
|
||||||
|
inout=In/Out
|
||||||
|
inputDescription=This store only produces input for data sources to read
|
||||||
|
outputDescription=This store only accepts output from data sources to write
|
||||||
|
inoutDescription=This store uses both input and output to essentially create a data transformation
|
Loading…
Reference in a new issue