mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 09:00:26 +00: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.TypedDataStreamParser;
|
||||
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
|
||||
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
|
||||
import io.xpipe.core.source.*;
|
||||
import io.xpipe.core.source.DataSourceId;
|
||||
import io.xpipe.core.source.DataSourceInfo;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
import io.xpipe.core.source.DataSourceType;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
@ -63,7 +65,7 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
|
|||
.ref(DataSourceReference.id(getId())).maxRows(maxRows).build();
|
||||
con.performInputExchange(req, (QueryTableDataExchange.Response res, InputStream in) -> {
|
||||
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);
|
||||
|
@ -78,7 +80,7 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
|
|||
private final TypedAbstractReader nodeReader;
|
||||
|
||||
{
|
||||
nodeReader = TypedReusableDataStructureNodeReader.create(info.getDataType());
|
||||
nodeReader = TypedDataStructureNodeReader.of(info.getDataType());
|
||||
parser = new TypedDataStreamParser(info.getDataType());
|
||||
|
||||
connection = XPipeConnection.open();
|
||||
|
|
|
@ -21,7 +21,7 @@ public class DataTableAccumulatorTest extends DaemonControl {
|
|||
var acc = DataTableAccumulator.create(type);
|
||||
|
||||
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);
|
||||
var table = acc.finish(":test");
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import io.xpipe.core.data.node.ValueNode;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GenericArrayReader implements GenericAbstractReader {
|
||||
|
||||
|
@ -127,9 +128,9 @@ public class GenericArrayReader implements GenericAbstractReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onValue(byte[] value, boolean textual) {
|
||||
public void onValue(byte[] value, Map<Integer, String> metaAttributes) {
|
||||
if (currentReader != null) {
|
||||
currentReader.onValue(value, textual);
|
||||
currentReader.onValue(value, metaAttributes);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -141,7 +142,7 @@ public class GenericArrayReader implements GenericAbstractReader {
|
|||
throw new IllegalStateException("Array is full but got another value");
|
||||
}
|
||||
|
||||
put(ValueNode.mutable(value, textual));
|
||||
put(ValueNode.of(value).tag(metaAttributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package io.xpipe.core.data.generic;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface GenericDataStreamCallback {
|
||||
|
||||
default void onName(String name) {
|
||||
|
@ -17,6 +19,7 @@ public interface GenericDataStreamCallback {
|
|||
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++) {
|
||||
parse(in, cb);
|
||||
}
|
||||
var attributes = DataStructureNodeIO.parseAttributes(in);
|
||||
cb.onTupleEnd();
|
||||
}
|
||||
|
||||
|
@ -75,19 +76,14 @@ public class GenericDataStreamParser {
|
|||
for (int i = 0; i < size; i++) {
|
||||
parse(in, cb);
|
||||
}
|
||||
var attributes = DataStructureNodeIO.parseAttributes(in);
|
||||
cb.onArrayEnd();
|
||||
}
|
||||
|
||||
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 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));
|
||||
write(out, tuple.at(i));
|
||||
}
|
||||
DataStructureNodeIO.writeAttributes(out, tuple);
|
||||
}
|
||||
|
||||
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++) {
|
||||
write(out, array.at(i));
|
||||
}
|
||||
DataStructureNodeIO.writeAttributes(out, array);
|
||||
}
|
||||
|
||||
private static void writeValue(OutputStream out, ValueNode n) throws IOException {
|
||||
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());
|
||||
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.ValueNode;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GenericDataStructureNodeReader implements GenericDataStreamCallback {
|
||||
|
||||
private DataStructureNode node;
|
||||
|
@ -78,12 +80,13 @@ public class GenericDataStructureNodeReader implements GenericDataStreamCallback
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onValue(byte[] value, boolean textual) {
|
||||
public void onValue(byte[] value, Map<Integer, String> metaAttributes) {
|
||||
if (hasReader()) {
|
||||
reader.onValue(value, textual);
|
||||
reader.onValue(value, metaAttributes);
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GenericTupleReader implements GenericAbstractReader {
|
||||
|
||||
|
@ -139,9 +140,9 @@ public class GenericTupleReader implements GenericAbstractReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onValue(byte[] value, boolean textual) {
|
||||
public void onValue(byte[] value, Map<Integer, String> metaAttributes) {
|
||||
if (currentReader != null) {
|
||||
currentReader.onValue(value, textual);
|
||||
currentReader.onValue(value, metaAttributes);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -153,7 +154,7 @@ public class GenericTupleReader implements GenericAbstractReader {
|
|||
throw new IllegalStateException("Tuple is full but got another value");
|
||||
}
|
||||
|
||||
putNode(ValueNode.mutable(value, textual));
|
||||
putNode(ValueNode.of(value).tag(metaAttributes));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,11 +17,7 @@ public abstract class ArrayNode extends DataStructureNode {
|
|||
}
|
||||
|
||||
public static ArrayNode of(List<DataStructureNode> nodes) {
|
||||
return new SimpleArrayNode(true, nodes);
|
||||
}
|
||||
|
||||
public static ArrayNode of(boolean mutable, List<DataStructureNode> nodes) {
|
||||
return new SimpleArrayNode(mutable, nodes);
|
||||
return new SimpleArrayNode(nodes);
|
||||
}
|
||||
|
||||
protected ArrayNode() {
|
||||
|
@ -29,8 +25,12 @@ public abstract class ArrayNode extends DataStructureNode {
|
|||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ArrayNode that)) return false;
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ArrayNode that)) {
|
||||
return false;
|
||||
}
|
||||
return getNodes().equals(that.getNodes());
|
||||
}
|
||||
|
||||
|
@ -49,18 +49,10 @@ public abstract class ArrayNode extends DataStructureNode {
|
|||
return "array node";
|
||||
}
|
||||
|
||||
@Override
|
||||
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 + "]";
|
||||
return "[" + content + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,20 +8,70 @@ import java.util.stream.Stream;
|
|||
|
||||
public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
||||
|
||||
public static final String KEY_TABLE_NAME = "tableName";
|
||||
public static final String KEY_ROW_NAME = "rowName";
|
||||
public static final Integer KEY_TABLE_NAME = 1;
|
||||
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) {
|
||||
if (properties == null) {
|
||||
public Map<Integer, String> getMetaAttributes() {
|
||||
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 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) {
|
||||
throw unsupported("key name at");
|
||||
|
@ -50,8 +100,6 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
|||
|
||||
public abstract boolean isMutable();
|
||||
|
||||
public abstract DataStructureNode immutableView();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(0);
|
||||
|
@ -61,26 +109,6 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
|||
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 boolean isTuple() {
|
||||
|
@ -99,6 +127,12 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
public DataStructureNode set(int index, DataStructureNode node) {
|
||||
throw unsupported("set at index");
|
||||
}
|
||||
|
||||
|
||||
public final ValueNode asValue() {
|
||||
if (!isValue()) {
|
||||
throw new UnsupportedOperationException(getName() + " is not a value node");
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
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 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_VALUE_ID = 8;
|
||||
|
||||
public static final int VALUE_TYPE_BARE = 0;
|
||||
public static final int VALUE_TYPE_TEXT = 1;
|
||||
public static final int VALUE_TYPE_NULL = 2;
|
||||
public static void writeString(OutputStream out, String s) throws IOException {
|
||||
if (s != null) {
|
||||
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
|
||||
protected String getIdentifier() {
|
||||
return "linked tuple node";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TupleNode mutableCopy() {
|
||||
public TupleNode mutable() {
|
||||
if (isMutable()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return new LinkedTupleNode(tupleNodes.stream().map(n -> n.isMutable() ? n : n.mutableCopy()).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TupleNode immutableView() {
|
||||
return this;
|
||||
return new LinkedTupleNode(tupleNodes.stream().map(n -> n.isMutable() ? n : n.mutable()).toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
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.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Value
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
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");
|
||||
}
|
||||
}
|
||||
List<DataStructureNode> nodes;
|
||||
|
||||
@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;
|
||||
}
|
||||
|
@ -47,34 +38,13 @@ public class SimpleArrayNode extends ArrayNode {
|
|||
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)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStructureNode clear() {
|
||||
checkMutable();
|
||||
|
||||
nodes.clear();
|
||||
return this;
|
||||
}
|
||||
|
@ -101,13 +71,11 @@ public class SimpleArrayNode extends ArrayNode {
|
|||
|
||||
@Override
|
||||
public List<DataStructureNode> getNodes() {
|
||||
return nodes;
|
||||
return Collections.unmodifiableList(nodes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataStructureNode remove(int index) {
|
||||
checkMutable();
|
||||
|
||||
nodes.remove(index);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
package io.xpipe.core.data.node;
|
||||
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SimpleImmutableValueNode extends ImmutableValueNode {
|
||||
public class SimpleImmutableValueNode extends ValueNode {
|
||||
|
||||
private final byte[] data;
|
||||
private final boolean textual;
|
||||
private final byte @NonNull [] data;
|
||||
|
||||
SimpleImmutableValueNode(byte[] data, boolean textual) {
|
||||
SimpleImmutableValueNode(byte @NonNull [] 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() {
|
||||
|
@ -28,10 +18,20 @@ public class SimpleImmutableValueNode extends ImmutableValueNode {
|
|||
|
||||
@Override
|
||||
public final String asString() {
|
||||
if (getRawData() == null) {
|
||||
if (getRawData().length == 0 && !hasMetaAttribute(TEXT)) {
|
||||
return "null";
|
||||
}
|
||||
|
||||
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.TupleType;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
public class SimpleTupleNode extends TupleNode {
|
||||
|
||||
private final boolean mutable;
|
||||
private final List<String> names;
|
||||
private final List<DataStructureNode> 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);
|
||||
public SimpleTupleNode(List<String> names, List<DataStructureNode> nodes) {
|
||||
this.names = names;
|
||||
this.nodes = 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;
|
||||
}
|
||||
|
@ -54,7 +36,7 @@ public class SimpleTupleNode extends TupleNode {
|
|||
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return mutable;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,8 +65,6 @@ public class SimpleTupleNode extends TupleNode {
|
|||
|
||||
@Override
|
||||
public DataStructureNode clear() {
|
||||
checkMutable();
|
||||
|
||||
nodes.clear();
|
||||
names.clear();
|
||||
return this;
|
||||
|
@ -118,7 +98,7 @@ public class SimpleTupleNode extends TupleNode {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected String getIdentifier() {
|
||||
return "S";
|
||||
public TupleNode mutable() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ public abstract class TupleNode extends DataStructureNode {
|
|||
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) {
|
||||
|
@ -30,41 +30,20 @@ public abstract class TupleNode extends DataStructureNode {
|
|||
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) {
|
||||
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(mutable, names, nodes);
|
||||
}
|
||||
@Override
|
||||
public abstract TupleNode mutable();
|
||||
|
||||
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 = "(" + getIdentifier() + ") {\n";
|
||||
var start = "{\n";
|
||||
var kvs = getKeyValuePairs().stream().map(kv -> {
|
||||
if (kv.key() == null) {
|
||||
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.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
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());
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof ValueNode that)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(getRawData(), that.getRawData()) && Objects.equals(getMetaAttributes(), that.getMetaAttributes());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,68 +29,18 @@ public abstract class ValueNode extends DataStructureNode {
|
|||
return Arrays.hashCode(getRawData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract ValueNode immutableView();
|
||||
|
||||
@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 nullValue() {
|
||||
return new SimpleImmutableValueNode(new byte[0]);
|
||||
}
|
||||
|
||||
public static ValueNode of(byte[] data) {
|
||||
return mutable(data, false);
|
||||
return new SimpleImmutableValueNode(data);
|
||||
}
|
||||
|
||||
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
|
||||
public final int asInt() {
|
||||
return Integer.parseInt(asString());
|
||||
|
@ -107,9 +63,4 @@ public abstract class ValueNode extends DataStructureNode {
|
|||
|
||||
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 {
|
||||
|
||||
default void onValue(byte[] data, boolean textual) {
|
||||
default void onValue(byte[] data) {
|
||||
}
|
||||
|
||||
default void onGenericNode(DataStructureNode node) {
|
||||
|
|
|
@ -129,15 +129,8 @@ public class TypedDataStreamParser {
|
|||
}
|
||||
|
||||
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 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 {
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -6,21 +6,15 @@ import io.xpipe.core.data.type.DataTypeVisitors;
|
|||
import io.xpipe.core.data.type.TupleType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
|
||||
public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
||||
|
||||
public static TypedDataStructureNodeReader mutable(DataType type) {
|
||||
return new TypedDataStructureNodeReader(type, false);
|
||||
public static TypedDataStructureNodeReader of(DataType type) {
|
||||
return new TypedDataStructureNodeReader(type);
|
||||
}
|
||||
|
||||
public static TypedDataStructureNodeReader immutable(DataType type) {
|
||||
return new TypedDataStructureNodeReader(type, true);
|
||||
}
|
||||
|
||||
private final boolean makeImmutable;
|
||||
private DataStructureNode readNode;
|
||||
|
||||
private final Stack<List<DataStructureNode>> children;
|
||||
|
@ -31,12 +25,11 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
private DataType expectedType;
|
||||
private int currentExpectedTypeIndex;
|
||||
|
||||
private TypedDataStructureNodeReader(DataType type, boolean makeImmutable) {
|
||||
private TypedDataStructureNodeReader(DataType type) {
|
||||
flattened = new ArrayList<>();
|
||||
type.visit(DataTypeVisitors.flatten(flattened::add));
|
||||
children = new Stack<>();
|
||||
nodes = new Stack<>();
|
||||
this.makeImmutable = makeImmutable;
|
||||
expectedType = flattened.get(0);
|
||||
}
|
||||
|
||||
|
@ -81,12 +74,12 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onValue(byte[] data, boolean textual) {
|
||||
public void onValue(byte[] data) {
|
||||
if (!expectedType.isValue()) {
|
||||
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);
|
||||
moveExpectedType(false);
|
||||
}
|
||||
|
@ -101,7 +94,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
throw new IllegalStateException("Expected " + expectedType.getName() + " but got generic node");
|
||||
}
|
||||
|
||||
finishNode(makeImmutable ? node.immutableView() : node);
|
||||
finishNode(node);
|
||||
moveExpectedType(false);
|
||||
}
|
||||
|
||||
|
@ -117,10 +110,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
var l = new ArrayList<DataStructureNode>(tupleType.getSize());
|
||||
children.push(l);
|
||||
|
||||
var tupleNames = makeImmutable ?
|
||||
Collections.unmodifiableList(tupleType.getNames()) : new ArrayList<>(tupleType.getNames());
|
||||
var tupleNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
|
||||
var newNode = TupleNode.of(!makeImmutable, tupleNames, tupleNodes);
|
||||
var newNode = new SimpleTupleNode(tupleType.getNames(), l);
|
||||
nodes.push(newNode);
|
||||
}
|
||||
|
||||
|
@ -159,8 +149,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
|
|||
var l = new ArrayList<DataStructureNode>();
|
||||
children.push(l);
|
||||
|
||||
var arrayNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
|
||||
var newNode = ArrayNode.of(arrayNodes);
|
||||
var newNode = ArrayNode.of(l);
|
||||
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")
|
||||
public interface DataStore {
|
||||
|
||||
default DataStoreFlow getFlow() {
|
||||
return DataStoreFlow.INOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public Charset getCharset() {
|
||||
return StandardCharsets.UTF_16LE;
|
||||
// TODO
|
||||
return StandardCharsets.ISO_8859_1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
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.GenericDataStreamWriter;
|
||||
import io.xpipe.core.data.generic.GenericDataStructureNodeReader;
|
||||
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.ValueNode;
|
||||
import io.xpipe.core.data.typed.TypedDataStreamParser;
|
||||
import io.xpipe.core.data.typed.TypedDataStreamWriter;
|
||||
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
|
||||
import io.xpipe.core.data.typed.TypedReusableDataStructureNodeReader;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
@ -19,7 +18,6 @@ import org.junit.jupiter.params.provider.EnumSource;
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class DataStructureTest {
|
||||
|
@ -91,74 +89,20 @@ public class DataStructureTest {
|
|||
|
||||
@ParameterizedTest
|
||||
@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) {
|
||||
var dataOut = new ByteArrayOutputStream();
|
||||
TypedDataStreamWriter.writeStructure(dataOut, node, ds.type);
|
||||
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);
|
||||
var readNode = reader.create();
|
||||
|
||||
Assertions.assertEquals(node, readNode);
|
||||
Assertions.assertDoesNotThrow(() -> {
|
||||
if (readNode.isTuple()) {
|
||||
readNode.clear();
|
||||
Assertions.assertEquals(readNode.size(), 0);
|
||||
}
|
||||
|
||||
if (readNode.isArray()) {
|
||||
readNode.clear();
|
||||
Assertions.assertEquals(readNode.size(), 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(DataStructureTests.TypedDataset.class)
|
||||
public void testImmutableTypedIo(DataStructureTests.TypedDataset ds) throws IOException {
|
||||
for (var node : ds.nodes) {
|
||||
var dataOut = new ByteArrayOutputStream();
|
||||
TypedDataStreamWriter.writeStructure(dataOut, node, ds.type);
|
||||
var data = dataOut.toByteArray();
|
||||
|
||||
var reader = TypedDataStructureNodeReader.immutable(ds.type);
|
||||
new TypedDataStreamParser(ds.type).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()) {
|
||||
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() {
|
||||
var val = ValueNode.of(null);
|
||||
var val = ValueNode.nullValue();
|
||||
var flatArray = ArrayNode.of();
|
||||
var flatTuple = TupleNode.builder().add("key1", val).build();
|
||||
var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
|
||||
|
@ -101,7 +101,7 @@ public class DataStructureTests {
|
|||
|
||||
public static DataStructureNode createTestData32() {
|
||||
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));
|
||||
return flatArray;
|
||||
}
|
||||
|
@ -112,13 +112,13 @@ public class DataStructureTests {
|
|||
}
|
||||
|
||||
public static DataStructureNode createTestData42() {
|
||||
var val = ValueNode.of(null);
|
||||
var val = ValueNode.nullValue();
|
||||
return val;
|
||||
}
|
||||
|
||||
public static DataStructureNode createTestData51() {
|
||||
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
|
||||
var flatArray = ArrayNode.of(List.of(val, ValueNode.of(null)));
|
||||
var flatArray = ArrayNode.of(List.of(val, ValueNode.nullValue()));
|
||||
var array1 = ArrayNode.of(List.of(flatArray));
|
||||
var array2 = ArrayNode.of(List.of(array1, array1));
|
||||
return array2;
|
||||
|
@ -143,7 +143,7 @@ public class DataStructureTests {
|
|||
|
||||
public static DataStructureNode createTestData61() {
|
||||
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()
|
||||
.add(val).add("key2", array).build();
|
||||
return tuple;
|
||||
|
@ -176,7 +176,7 @@ public class DataStructureTests {
|
|||
|
||||
public static DataStructureNode createTestData73() {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.dialog.Dialog;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.MachineFileStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
import io.xpipe.core.store.*;
|
||||
import javafx.beans.property.Property;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -91,4 +88,8 @@ public interface DataStoreProvider {
|
|||
}
|
||||
|
||||
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.dialog.Dialog;
|
||||
import io.xpipe.core.dialog.QueryConverter;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.LocalStore;
|
||||
import io.xpipe.core.store.MachineFileStore;
|
||||
import io.xpipe.core.store.ShellStore;
|
||||
import io.xpipe.core.source.DataSource;
|
||||
import io.xpipe.core.store.*;
|
||||
import io.xpipe.core.util.Secret;
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class DialogHelper {
|
||||
|
||||
@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) {
|
||||
var storeName = XPipeDaemon.getInstance()
|
||||
.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) {
|
||||
return Dialog.querySecret("Password", false, true, password);
|
||||
}
|
||||
|
|
|
@ -53,5 +53,9 @@ public interface SupportedApplicationProvider {
|
|||
default String getGraphicIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default boolean isApplicable(DataSource<?> source) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.source.DataSource;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.fxcomps.Comp;
|
||||
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<?> namedSourceChooser(ObservableValue<Predicate<DataSource<?>>> filter, Property<? extends DataSource<?>> selected, DataSourceProvider.Category category);
|
||||
|
||||
Comp<?> sourceProviderChooser(Property<DataSourceProvider<?>> provider, DataSourceProvider.Category category);
|
||||
|
||||
Optional<DataStore> getNamedStore(String name);
|
||||
|
||||
Optional<DataSource<?>> getSource(String id);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
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
|
||||
namedHostNotActive=$HOST$ is not active
|
||||
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