Rework types

This commit is contained in:
Christopher Schnick 2021-12-18 12:58:07 +01:00
parent a6bb824bd3
commit df9495e0a3
43 changed files with 466 additions and 444 deletions

View file

@ -1,6 +1,6 @@
package io.xpipe.api;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
public interface XPipeDataStructureSource {

View file

@ -8,12 +8,14 @@ import io.xpipe.beacon.ConnectorException;
import io.xpipe.beacon.ServerException;
import io.xpipe.beacon.exchange.ReadTableDataExchange;
import io.xpipe.beacon.exchange.ReadTableInfoExchange;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.DataType;
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.DataSourceId;
import java.io.IOException;
@ -26,9 +28,11 @@ import java.util.stream.StreamSupport;
public class DataTableImpl implements DataTable {
public static DataTable get(String s) {
final DataTable[] table = {null};
return get(DataSourceId.fromString(s));
}
var ds = DataSourceId.fromString(s);
public static DataTable get(DataSourceId ds) {
final DataTable[] table = {null};
new XPipeApiConnector() {
@Override
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
@ -95,7 +99,7 @@ public class DataTableImpl implements DataTable {
var req = new ReadTableDataExchange.Request(id, maxToRead);
performExchange(sc, req, (ReadTableDataExchange.Response res, InputStream in) -> {
var r = new TypedDataStreamParser(dataType);
r.readStructures(in, new TypedDataStructureNodeReader(dataType), nodes::add);
r.parseStructures(in, TypedDataStructureNodeReader.immutable(dataType), nodes::add);
}, false);
}
}.execute();
@ -104,28 +108,26 @@ public class DataTableImpl implements DataTable {
@Override
public Iterator<TupleNode> iterator() {
return new Iterator<TupleNode>() {
return new Iterator<>() {
private InputStream input;
private int read;
private final int toRead = size;
private TypedDataStreamParser reader;
private TypedDataStructureNodeReader nodeReader;
private TupleNode current;
private final TypedDataStreamParser parser;
private final TypedAbstractReader nodeReader;
{
new XPipeApiConnector() {
@Override
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
var req = new ReadTableDataExchange.Request(id, Integer.MAX_VALUE);
performExchange(sc, req, (ReadTableDataExchange.Response res, InputStream in) -> {
input = in;
}, false);
performExchange(sc, req,
(ReadTableDataExchange.Response res, InputStream in) -> input = in, false);
}
}.execute();
nodeReader = new TypedDataStructureNodeReader(dataType);
reader = new TypedDataStreamParser(dataType);
nodeReader = TypedReusableDataStructureNodeReader.create(dataType);
parser = new TypedDataStreamParser(dataType);
}
private boolean hasKnownSize() {
@ -143,7 +145,7 @@ public class DataTableImpl implements DataTable {
}
try {
return reader.hasNext(input);
return parser.hasNext(input);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
@ -151,8 +153,9 @@ public class DataTableImpl implements DataTable {
@Override
public TupleNode next() {
TupleNode current;
try {
current = (TupleNode) reader.readStructure(input, nodeReader);
current = (TupleNode) parser.parseStructure(input, nodeReader);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
public interface GenericAbstractReader extends GenericDataStreamCallback {

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.ValueNode;

View file

@ -1,7 +1,7 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.DataStructureNodeIO;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeIO;
import java.io.IOException;
import java.io.InputStream;

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ValueNode;
public class GenericDataStructureNodeReader implements GenericDataStreamCallback {

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
@ -130,7 +130,7 @@ public class GenericTupleReader implements GenericAbstractReader {
throw new IllegalStateException("Tuple ended but is not full yet");
}
created = TupleNode.wrap(names, nodes);
created = TupleNode.of(names, nodes);
}
@Override
@ -162,6 +162,6 @@ public class GenericTupleReader implements GenericAbstractReader {
throw new IllegalStateException();
}
return SimpleTupleNode.wrap(names, nodes);
return SimpleTupleNode.of(names, nodes);
}
}

View file

@ -1,6 +1,5 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.ArrayType;
import lombok.EqualsAndHashCode;
@ -70,7 +69,7 @@ public class ArrayNode extends DataStructureNode {
@Override
public ArrayType determineDataType() {
return ArrayType.of(valueNodes.stream().map(DataStructureNode::determineDataType).toList());
return ArrayType.ofSharedType(valueNodes.stream().map(DataStructureNode::determineDataType).toList());
}
@Override

View file

@ -1,8 +1,5 @@
package io.xpipe.core.data;
package io.xpipe.core.data.node;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.type.DataType;
import java.util.Iterator;

View file

@ -1,4 +1,4 @@
package io.xpipe.core.data;
package io.xpipe.core.data.node;
public interface DataStructureNodeAcceptor<T extends DataStructureNode> {

View file

@ -1,4 +1,4 @@
package io.xpipe.core.data;
package io.xpipe.core.data.node;
public class DataStructureNodeIO {
@ -8,8 +8,8 @@ public class DataStructureNodeIO {
public static final int GENERIC_VALUE_ID = 3;
public static final int GENERIC_NAME_ID = 4;
public static final int TYPED_STRUCTURE_ID = 0;
public static final int TYPED_TUPLE_ID = 5;
public static final int TYPED_ARRAY_ID = 6;
public static final int TYPED_VALUE_ID = 7;
public static final int TYPED_STRUCTURE_ID = 5;
public static final int TYPED_TUPLE_ID = 6;
public static final int TYPED_ARRAY_ID = 7;
public static final int TYPED_VALUE_ID = 8;
}

View file

@ -1,4 +1,4 @@
package io.xpipe.core.data;
package io.xpipe.core.data.node;
import java.util.ArrayList;
import java.util.List;

View file

@ -1,7 +1,5 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
public class ImmutableValueNode extends ValueNode {
private final byte[] data;

View file

@ -1,7 +1,5 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
public class MutableValueNode extends ValueNode {
private byte[] data;

View file

@ -1,6 +1,5 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;

View file

@ -1,6 +1,5 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import lombok.EqualsAndHashCode;

View file

@ -1,6 +1,5 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
import lombok.Value;
import java.util.ArrayList;
@ -14,7 +13,7 @@ public abstract class TupleNode extends DataStructureNode {
return new Builder();
}
public static TupleNode wrap(List<DataStructureNode> nodes) {
public static TupleNode of(List<DataStructureNode> nodes) {
if (nodes == null) {
throw new IllegalArgumentException("Nodes must be not null");
}
@ -22,11 +21,11 @@ public abstract class TupleNode extends DataStructureNode {
return new NoKeyTupleNode(nodes);
}
public static TupleNode copy(List<DataStructureNode> nodes) {
return TupleNode.wrap(List.copyOf(nodes));
public static TupleNode copyOf(List<DataStructureNode> nodes) {
return TupleNode.of(List.copyOf(nodes));
}
public static TupleNode wrap(List<String> names, List<DataStructureNode> nodes) {
public static TupleNode of(List<String> names, List<DataStructureNode> nodes) {
if (names == null) {
throw new IllegalArgumentException("Names must be not null");
}
@ -40,7 +39,7 @@ public abstract class TupleNode extends DataStructureNode {
return new SimpleTupleNode(names, nodes);
}
public static TupleNode wrapRaw(List<String> names, List<DataStructureNode> nodes) {
public static TupleNode ofRaw(List<String> names, List<DataStructureNode> nodes) {
if (names == null) {
throw new IllegalArgumentException("Names must be not null");
}
@ -50,8 +49,8 @@ public abstract class TupleNode extends DataStructureNode {
return new SimpleTupleNode(names, nodes);
}
public static TupleNode copy(List<String> names, List<DataStructureNode> nodes) {
return TupleNode.wrap(List.copyOf(names), List.copyOf(nodes));
public static TupleNode copyOf(List<String> names, List<DataStructureNode> nodes) {
return TupleNode.of(List.copyOf(names), List.copyOf(nodes));
}
public final boolean isTuple() {
@ -106,10 +105,10 @@ public abstract class TupleNode extends DataStructureNode {
public TupleNode build() {
boolean hasKeys = entries.stream().anyMatch(kv -> kv.key != null);
return hasKeys ? TupleNode.wrap(
return hasKeys ? TupleNode.of(
entries.stream().map(kv -> kv.key).toList(),
entries.stream().map(kv -> kv.value).toList()) :
TupleNode.wrap(entries.stream().map(kv -> kv.value).toList());
TupleNode.of(entries.stream().map(kv -> kv.value).toList());
}
}
}

View file

@ -1,6 +1,5 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.ValueType;
import lombok.EqualsAndHashCode;
@ -19,6 +18,10 @@ public abstract class ValueNode extends DataStructureNode {
return new ImmutableValueNode(data);
}
public static ValueNode immutable(Object o) {
return immutable(o.toString().getBytes(StandardCharsets.UTF_8));
}
public static ValueNode mutableNull() {
return mutable(NULL);
}

View file

@ -1,27 +1,38 @@
package io.xpipe.core.data.type;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.callback.DataTypeCallback;
import io.xpipe.core.data.node.DataStructureNode;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Value;
import java.util.List;
/**
* An array type represents an array of {@link DataStructureNode} of a certain shared type.
* The shared type should be the most specific data type possible.
*/
@JsonTypeName("array")
@EqualsAndHashCode
public class ArrayType implements DataType {
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Value
public class ArrayType extends DataType {
private final DataType sharedType;
DataType sharedType;
public ArrayType(DataType sharedType) {
this.sharedType = sharedType;
/**
* Creates a new array type for a given shared data type.
*/
public static ArrayType of(DataType type) {
return new ArrayType(type);
}
public static ArrayType ofWildcard() {
return new ArrayType(WildcardType.of());
}
public static ArrayType of(List<DataType> types) {
/**
* Creates a new array type using either the shared type of {@code types}
* or a wildcard type if the elements do not share a common type.
*/
public static ArrayType ofSharedType(List<DataType> types) {
if (types.size() == 0) {
return new ArrayType(WildcardType.of());
}
@ -31,18 +42,6 @@ public class ArrayType implements DataType {
return new ArrayType(eq ? first : WildcardType.of());
}
public boolean isSimple() {
return hasSharedType() && getSharedType().isValue();
}
public boolean hasSharedType() {
return sharedType != null;
}
public DataType getSharedType() {
return sharedType;
}
@Override
public String getName() {
return "array";
@ -50,7 +49,17 @@ public class ArrayType implements DataType {
@Override
public boolean matches(DataStructureNode node) {
return node.isArray();
if (!node.isArray()) {
return false;
}
var a = node.asArray();
for (var n : a) {
if (!sharedType.matches(n)) {
return false;
}
}
return true;
}
@Override
@ -59,7 +68,7 @@ public class ArrayType implements DataType {
}
@Override
public void traverseType(DataTypeCallback cb) {
cb.onArray(this);
public void visit(DataTypeVisitor visitor) {
visitor.onArray(this);
}
}

View file

@ -1,31 +1,108 @@
package io.xpipe.core.data.type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.callback.DataTypeCallback;
import io.xpipe.core.data.node.DataStructureNode;
/**
* Represents the type of a {@link DataStructureNode} object.
* To check whether a {@link DataStructureNode} instance conforms to the specified type,
* the method {@link #matches(DataStructureNode)} can be used.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface DataType {
public abstract class DataType {
String getName();
/**
* Returns the readable name of this data type that can be used for error messages.
*/
public abstract String getName();
boolean matches(DataStructureNode node);
/**
* Checks whether a node conforms to this data type.
*/
public abstract boolean matches(DataStructureNode node);
default boolean isTuple() {
/**
* Checks whether this type is a tuple.
*/
public boolean isTuple() {
return false;
}
default boolean isWildcard() {
/**
* Checks whether this type is a wildcard.
*/
public boolean isWildcard() {
return false;
}
default boolean isArray() {
/**
* Checks whether this type is an array.
*/
public boolean isArray() {
return false;
}
default boolean isValue() {
/**
* Checks whether this type is a value.
*/
public boolean isValue() {
return false;
}
void traverseType(DataTypeCallback cb);
/**
* Casts this type to a wildcard type if possible.
*
* @throws UnsupportedOperationException if the type is not a wildcard type
*/
public final WildcardType asWildcard() {
if (!isWildcard()) {
throw new UnsupportedOperationException(getName() + " is not a wildcard type");
}
return (WildcardType) this;
}
/**
* Casts this type to a value type if possible.
*
* @throws UnsupportedOperationException if the type is not a value type
*/
public final ValueType asValue() {
if (!isValue()) {
throw new UnsupportedOperationException(getName() + " is not a value type");
}
return (ValueType) this;
}
/**
* Casts this type to a tuple type if possible.
*
* @throws UnsupportedOperationException if the type is not a tuple type
*/
public final TupleType asTuple() {
if (!isTuple()) {
throw new UnsupportedOperationException(getName() + " is not a tuple type");
}
return (TupleType) this;
}
/**
* Casts this type to an array type if possible.
*
* @throws UnsupportedOperationException if the type is not an array type
*/
public final ArrayType asArray() {
if (!isArray()) {
throw new UnsupportedOperationException(getName() + " is not an array type");
}
return (ArrayType) this;
}
/**
* Visits this type using a {@link DataTypeVisitor} instance.
*/
public abstract void visit(DataTypeVisitor visitor);
}

View file

@ -0,0 +1,16 @@
package io.xpipe.core.data.type;
public interface DataTypeVisitor {
default void onValue(ValueType type) {
}
default void onTuple(TupleType type) {
}
default void onArray(ArrayType type) {
}
default void onWildcard(WildcardType type) {
}
}

View file

@ -0,0 +1,106 @@
package io.xpipe.core.data.type;
import io.xpipe.core.data.node.DataStructureNodePointer;
import java.util.Stack;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class DataTypeVisitors {
/**
* Creates a visitor that sequentially visits all subtypes.
*/
public static DataTypeVisitor flatten(Consumer<DataType> typeConsumer) {
return new DataTypeVisitor() {
@Override
public void onValue(ValueType type) {
typeConsumer.accept(type);
}
@Override
public void onTuple(TupleType type) {
typeConsumer.accept(type);
type.getTypes().forEach(t -> t.visit(this));
}
@Override
public void onArray(ArrayType type) {
typeConsumer.accept(type);
}
@Override
public void onWildcard(WildcardType type) {
typeConsumer.accept(type);
}
};
}
/**
* Creates a visitor that allows for visiting possible recursive columns of table.
*/
public static DataTypeVisitor table(
Consumer<String> newTuple,
Runnable endTuple,
BiConsumer<String, DataStructureNodePointer> newValue) {
return new DataTypeVisitor() {
private final Stack<TupleType> tuples = new Stack<>();
private final Stack<Integer> keyIndices = new Stack<>();
private boolean isOnTopLevel() {
return tuples.size() <= 1;
}
private void onAnyValue() {
var pointer = DataStructureNodePointer.builder();
for (int index : keyIndices) {
pointer.index(index);
}
var p = pointer.build();
newValue.accept(tuples.peek().getNames().get(keyIndices.peek()), p);
moveIndex();
}
private void moveIndex() {
var index = keyIndices.pop();
index++;
keyIndices.push(index);
}
@Override
public void onValue(ValueType type) {
onAnyValue();
}
@Override
public void onWildcard(WildcardType type) {
onAnyValue();
}
@Override
public void onTuple(TupleType tuple) {
if (!isOnTopLevel()) {
moveIndex();
}
tuples.push(tuple);
keyIndices.push(0);
if (!isOnTopLevel()) {
newTuple.accept(tuples.peek().getNames().get(keyIndices.peek()));
}
tuple.getTypes().forEach(t -> t.visit(this));
endTuple.run();
tuples.pop();
keyIndices.pop();
}
@Override
public void onArray(ArrayType type) {
onAnyValue();
}
};
}
}

View file

@ -2,35 +2,51 @@ package io.xpipe.core.data.type;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.callback.DataTypeCallback;
import io.xpipe.core.data.node.DataStructureNode;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Value;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A tuple type in the context of XPipe is defined as an ordered,
* fixed-size sequence of elements that can be optionally assigned a name.
* This permissive design allows for a very flexible usage of the tuple type.
*/
@JsonTypeName("tuple")
@EqualsAndHashCode
public class TupleType implements DataType {
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Value
public class TupleType extends DataType {
private final List<String> names;
private final List<DataType> types;
@JsonCreator
private TupleType(List<String> names, List<DataType> types) {
this.names = names;
this.types = types;
}
List<String> names;
List<DataType> types;
/**
* Creates a new tuple type that contains no entries.
*/
public static TupleType empty() {
return new TupleType(List.of(), List.of());
}
/**
* Creates a new tuple type for a list of names and types.
* Any entry in {@code names} can be null, while any element in {@code types} must be not null.
*/
@JsonCreator
public static TupleType of(List<String> names, List<DataType> types) {
return new TupleType(names, types);
}
public static TupleType wrapWithoutNames(List<DataType> types) {
/**
* Creates a new tuple type for a list of types with no names.
* Any element in {@code types} must be not null.
*/
public static TupleType of(List<DataType> types) {
return new TupleType(Collections.nCopies(types.size(), null), types);
}
@ -41,7 +57,28 @@ public class TupleType implements DataType {
@Override
public boolean matches(DataStructureNode node) {
return node.isTuple();
if (!node.isTuple()) {
return false;
}
var t = node.asTuple();
if (t.size() != getSize()) {
return false;
}
int counter = 0;
for (var kv : t.getKeyValuePairs()) {
if (!Objects.equals(kv.getKey(), names.get(counter))) {
return false;
}
if (!types.get(counter).matches(kv.getValue())) {
return false;
}
counter++;
}
return true;
}
@Override
@ -50,23 +87,11 @@ public class TupleType implements DataType {
}
@Override
public void traverseType(DataTypeCallback cb) {
cb.onTupleBegin(this);
for (var t : types) {
t.traverseType(cb);
}
cb.onTupleEnd();
public void visit(DataTypeVisitor visitor) {
visitor.onTuple(this);
}
public int getSize() {
return types.size();
}
public List<String> getNames() {
return names;
}
public List<DataType> getTypes() {
return types;
}
}

View file

@ -1,18 +1,24 @@
package io.xpipe.core.data.type;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.callback.DataTypeCallback;
import io.xpipe.core.data.node.DataStructureNode;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Value;
/**
* A value type represents any node that holds some atomic value, i.e. it has no subtypes.
*/
@JsonTypeName("value")
@EqualsAndHashCode
public class ValueType implements DataType {
private ValueType() {
}
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Value
public class ValueType extends DataType {
/**
* Creates a new instance.
*/
public static ValueType of() {
return new ValueType();
}
@ -33,7 +39,7 @@ public class ValueType implements DataType {
}
@Override
public void traverseType(DataTypeCallback cb) {
cb.onValue();
public void visit(DataTypeVisitor visitor) {
visitor.onValue(this);
}
}

View file

@ -1,18 +1,29 @@
package io.xpipe.core.data.type;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.callback.DataTypeCallback;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.data.node.DataStructureNode;
import lombok.EqualsAndHashCode;
import lombok.Value;
public class WildcardType implements DataType {
private WildcardType() {
}
/**
* A wildcard type matches any {@link DataStructureNode} instance.
* For simplicity reasons it is not possible to further specify a wildcard instance to only match a certain
* subset of {@link DataStructureNode} instance that fullfil a certain property.
*/
@JsonTypeName("wildcard")
@EqualsAndHashCode(callSuper = false)
@Value
public class WildcardType extends DataType {
/**
* Creates a new instance.
*/
public static WildcardType of() {
return new WildcardType();
}
private WildcardType() {}
@Override
public String getName() {
return "wildcard";
@ -29,7 +40,7 @@ public class WildcardType implements DataType {
}
@Override
public void traverseType(DataTypeCallback cb) {
public void visit(DataTypeVisitor visitor) {
visitor.onWildcard(this);
}
}

View file

@ -1,42 +0,0 @@
package io.xpipe.core.data.type.callback;
import io.xpipe.core.data.type.ArrayType;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.type.ValueType;
import java.util.function.Consumer;
public interface DataTypeCallback {
static DataTypeCallback flatten(Consumer<DataType> typeConsumer) {
return new DataTypeCallback() {
@Override
public void onValue() {
typeConsumer.accept(ValueType.of());
}
@Override
public void onTupleBegin(TupleType tuple) {
typeConsumer.accept(tuple);
}
@Override
public void onArray(ArrayType type) {
typeConsumer.accept(type);
}
};
}
default void onValue() {
}
default void onTupleBegin(TupleType tuple) {
}
default void onTupleEnd() {
}
default void onArray(ArrayType type) {
}
}

View file

@ -1,61 +0,0 @@
package io.xpipe.core.data.type.callback;
import io.xpipe.core.data.DataStructureNodePointer;
import io.xpipe.core.data.type.ArrayType;
import io.xpipe.core.data.type.TupleType;
import java.util.Stack;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class DataTypeCallbacks {
public static DataTypeCallback visitTuples(Consumer<String> newTuple, Runnable endTuple, BiConsumer<String, DataStructureNodePointer> newValue) {
return new DataTypeCallback() {
private final Stack<String> keyNames = new Stack<>();
private final Stack<DataStructureNodePointer.Builder> builders = new Stack<>();
{
builders.push(DataStructureNodePointer.builder());
}
private boolean isOnTopLevel() {
return keyNames.size() == 0;
}
@Override
public void onTupleBegin(TupleType tuple) {
if (!isOnTopLevel()) {
newTuple.accept(keyNames.peek());
}
tuple.getNames().forEach(n -> {
keyNames.push(n);
builders.push(builders.peek().copy().name(n));
tuple.getTypes().forEach(dt -> dt.traverseType(this));
});
}
@Override
public void onValue() {
newValue.accept(keyNames.peek(), builders.peek().build());
keyNames.pop();
builders.pop();
}
@Override
public void onTupleEnd() {
endTuple.run();
}
@Override
public void onArray(ArrayType type) {
if (!type.isSimple()) {
throw new IllegalStateException();
}
newValue.accept(keyNames.peek(), builders.peek().build());
}
};
}
}

View file

@ -1,63 +0,0 @@
package io.xpipe.core.data.type.callback;
import io.xpipe.core.data.type.TupleType;
public class FlatArrayTypeCallback implements DataTypeCallback {
private final FlatCallback cb;
private int arrayDepth = 0;
public FlatArrayTypeCallback(FlatCallback cb) {
this.cb = cb;
}
private boolean isInArray() {
return arrayDepth > 0;
}
@Override
public void onValue() {
if (isInArray()) {
return;
}
cb.onValue();
}
@Override
public void onTupleBegin(TupleType tuple) {
if (isInArray()) {
throw new IllegalStateException();
}
cb.onTupleBegin(tuple);
}
@Override
public void onTupleEnd() {
cb.onTupleEnd();
}
public void onArray() {
if (isInArray()) {
throw new IllegalStateException();
}
arrayDepth++;
}
public interface FlatCallback {
default void onValue() {
}
default void onTupleBegin(TupleType tuple) {
}
default void onTupleEnd() {
}
default void onFlatArray() {
}
}
}

View file

@ -1,80 +0,0 @@
package io.xpipe.core.data.type.callback;
import io.xpipe.core.data.DataStructureNodePointer;
import io.xpipe.core.data.type.ArrayType;
import io.xpipe.core.data.type.TupleType;
import java.util.Stack;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class TableTypeCallback implements DataTypeCallback {
private final Stack<TupleType> tuples = new Stack<>();
private final Stack<Integer> keyIndices = new Stack<>();
private final Consumer<String> newTuple;
private final Runnable endTuple;
private final BiConsumer<String, DataStructureNodePointer> newValue;
private TableTypeCallback(Consumer<String> newTuple, Runnable endTuple, BiConsumer<String, DataStructureNodePointer> newValue) {
this.newTuple = newTuple;
this.endTuple = endTuple;
this.newValue = newValue;
}
public static DataTypeCallback create(Consumer<String> newTuple, Runnable endTuple, BiConsumer<String, DataStructureNodePointer> newValue) {
return new TableTypeCallback(newTuple, endTuple, newValue);
}
private boolean isOnTopLevel() {
return tuples.size() <= 1;
}
private void onAnyValue() {
var pointer = DataStructureNodePointer.builder();
for (int index : keyIndices) {
pointer.index(index);
}
var p = pointer.build();
newValue.accept(tuples.peek().getNames().get(keyIndices.peek()), p);
moveIndex();
}
private void moveIndex() {
var index = keyIndices.pop();
index++;
keyIndices.push(index);
}
@Override
public void onValue() {
onAnyValue();
}
@Override
public void onTupleBegin(TupleType tuple) {
if (!isOnTopLevel()) {
moveIndex();
}
tuples.push(tuple);
keyIndices.push(0);
if (!isOnTopLevel()) {
newTuple.accept(tuples.peek().getNames().get(keyIndices.peek()));
}
}
@Override
public void onTupleEnd() {
endTuple.run();
tuples.pop();
keyIndices.pop();
}
@Override
public void onArray(ArrayType type) {
onAnyValue();
}
}

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
public interface TypedAbstractReader extends TypedDataStreamCallback {

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.type.TupleType;
import java.io.IOException;

View file

@ -1,7 +1,7 @@
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.DataStructureNodeIO;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeIO;
import io.xpipe.core.data.generic.GenericDataStreamParser;
import io.xpipe.core.data.generic.GenericDataStructureNodeReader;
import io.xpipe.core.data.type.ArrayType;
@ -34,27 +34,31 @@ public class TypedDataStreamParser {
return true;
}
public void readStructures(InputStream in, TypedAbstractReader cb, Consumer<DataStructureNode> consumer) throws IOException {
public void parseStructures(InputStream in, TypedAbstractReader cb, Consumer<DataStructureNode> consumer) throws IOException {
while (hasNext(in)) {
cb.onNodeBegin();
read(in, cb, dataType);
parse(in, cb, dataType);
cb.onNodeEnd();
consumer.accept(cb.create());
}
}
public DataStructureNode readStructure(InputStream in, TypedAbstractReader cb) throws IOException {
public DataStructureNode parseStructure(InputStream in, TypedAbstractReader cb) throws IOException {
if (!hasNext(in)) {
throw new IllegalStateException("No structure to read");
}
cb.onNodeBegin();
read(in, cb, dataType);
parse(in, cb, dataType);
cb.onNodeEnd();
return cb.create();
}
private void read(InputStream in, TypedDataStreamCallback cb, DataType type) throws IOException {
public void parse(InputStream in, TypedDataStreamCallback cb) throws IOException {
parse(in, cb, dataType);
}
private void parse(InputStream in, TypedDataStreamCallback cb, DataType type) throws IOException {
var b = in.read();
// Skip
@ -69,7 +73,7 @@ public class TypedDataStreamParser {
}
var tt = (TupleType) type;
readTypedTuple(in, cb, tt);
parseTypedTuple(in, cb, tt);
}
case DataStructureNodeIO.TYPED_ARRAY_ID -> {
if (!type.isArray()) {
@ -77,20 +81,23 @@ public class TypedDataStreamParser {
}
var at = (ArrayType) type;
readTypedArray(in, cb, at);
parseTypedArray(in, cb, at);
}
case DataStructureNodeIO.TYPED_VALUE_ID -> {
if (!type.isValue()) {
throw new IllegalStateException("Got value but expected " + type.getName());
}
readValue(in, cb);
parseValue(in, cb);
}
default -> {
GenericDataStreamParser.
throw new IllegalStateException("Unexpected value: " + b);
}
default -> throw new IllegalStateException("Unexpected value: " + b);
}
}
private void readTypedTuple(InputStream in, TypedDataStreamCallback cb, TupleType type) throws IOException {
private void parseTypedTuple(InputStream in, TypedDataStreamCallback cb, TupleType type) throws IOException {
cb.onTupleBegin(type);
for (int i = 0; i < type.getSize(); i++) {
if (type.getTypes().get(i).isWildcard()) {
@ -99,7 +106,7 @@ public class TypedDataStreamParser {
var node = r.create();
cb.onGenericNode(node);
} else {
read(in, cb, type.getTypes().get(i));
parse(in, cb, type.getTypes().get(i));
}
}
cb.onTupleEnd();
@ -112,7 +119,7 @@ public class TypedDataStreamParser {
return genericReader;
}
private void readTypedArray(InputStream in, TypedDataStreamCallback cb, ArrayType type) throws IOException {
private void parseTypedArray(InputStream in, TypedDataStreamCallback cb, ArrayType type) throws IOException {
var size = in.read();
cb.onArrayBegin(size);
for (int i = 0; i < size; i++) {
@ -122,13 +129,13 @@ public class TypedDataStreamParser {
var node = r.create();
cb.onGenericNode(node);
} else {
read(in, cb, type.getSharedType());
parse(in, cb, type.getSharedType());
}
}
cb.onArrayEnd();
}
private void readValue(InputStream in, TypedDataStreamCallback cb) throws IOException {
private void parseValue(InputStream in, TypedDataStreamCallback cb) throws IOException {
var size = in.read();
var data = in.readNBytes(size);
cb.onValue(data);

View file

@ -1,7 +1,7 @@
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.DataStructureNodeIO;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeIO;
import io.xpipe.core.data.generic.GenericDataStreamWriter;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
@ -27,8 +27,10 @@ public class TypedDataStreamWriter {
writeArray(out, (ArrayNode) node, (ArrayType) type);
} else if (node.isValue() && type.isValue()) {
writeValue(out, (ValueNode) node);
} else if (type.isWildcard()) {
GenericDataStreamWriter.write(out, node);
} else {
throw new AssertionError();
throw new IllegalStateException("Incompatible node and type");
}
}
@ -45,11 +47,7 @@ public class TypedDataStreamWriter {
out.write(DataStructureNodeIO.TYPED_TUPLE_ID);
for (int i = 0; i < tuple.size(); i++) {
if (!type.getTypes().get(i).isWildcard()) {
write(out, tuple.at(i), type.getTypes().get(i));
} else {
GenericDataStreamWriter.write(out, tuple.at(i));
}
write(out, tuple.at(i), type.getTypes().get(i));
}
}
@ -57,11 +55,7 @@ public class TypedDataStreamWriter {
out.write(DataStructureNodeIO.TYPED_ARRAY_ID);
out.write(array.size());
for (int i = 0; i < array.size(); i++) {
if (!type.getSharedType().isWildcard()) {
write(out, array.at(i), type.getSharedType());
} else {
GenericDataStreamWriter.write(out, array.at(i));
}
write(out, array.at(i), type.getSharedType());
}
}
}

View file

@ -1,13 +1,13 @@
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.type.callback.DataTypeCallback;
import io.xpipe.core.data.type.DataTypeVisitors;
import java.io.IOException;
import java.util.ArrayList;
@ -29,7 +29,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
flattened = new ArrayList<>();
children = new Stack<>();
nodes = new Stack<>();
type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d)));
type.visit(DataTypeVisitors.flatten(d -> flattened.add(d)));
this.makeImmutable = makeImmutable;
}
@ -114,7 +114,7 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
var tupleNames = makeImmutable ?
Collections.unmodifiableList(tupleType.getNames()) : new ArrayList<>(tupleType.getNames());
var tupleNodes = makeImmutable ? Collections.unmodifiableList(l) : l;
var newNode = TupleNode.wrapRaw(tupleNames, tupleNodes);
var newNode = TupleNode.ofRaw(tupleNames, tupleNodes);
nodes.push(newNode);
}

View file

@ -1,10 +1,10 @@
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.type.callback.DataTypeCallback;
import io.xpipe.core.data.type.DataTypeVisitors;
import java.io.IOException;
import java.util.ArrayList;
@ -13,17 +13,21 @@ 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;
public TypedReusableDataStructureNodeReader(DataType type) {
private TypedReusableDataStructureNodeReader(DataType type) {
flattened = new ArrayList<>();
indices = new Stack<>();
initialReader = TypedDataStructureNodeReader.mutable(type);
type.traverseType(DataTypeCallback.flatten(d -> flattened.add(d)));
type.visit(DataTypeVisitors.flatten(d -> flattened.add(d)));
}
@Override

View file

@ -1,7 +1,7 @@
package io.xpipe.core.source;
import io.xpipe.core.data.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.TupleType;

View file

@ -4,9 +4,9 @@ import io.xpipe.core.store.DataStore;
public abstract class TableDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> {
public abstract TableDataWriteConnection openWriteConnection(DS store);
public abstract TableDataWriteConnection newWriteConnection(DS store);
public abstract TableDataReadConnection openConnection(DS store);
public abstract TableDataReadConnection newReadConnection(DS store);
@Override
public DataSourceType getType() {

View file

@ -1,12 +1,12 @@
package io.xpipe.core.source;
import io.xpipe.core.data.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.node.TupleNode;
public interface TableDataWriteConnection extends DataSourceConnection {
DataStructureNodeAcceptor<SimpleTupleNode> writeLinesAcceptor();
DataStructureNodeAcceptor<TupleNode> writeLinesAcceptor();
void writeLines(ArrayNode lines) throws Exception;
}

View file

@ -16,10 +16,6 @@ module io.xpipe.core {
opens io.xpipe.core.source;
opens io.xpipe.core.data.type;
opens io.xpipe.core.data.generic;
exports io.xpipe.core.data.type.callback;
opens io.xpipe.core.data.type.callback;
exports io.xpipe.core.data;
opens io.xpipe.core.data;
exports io.xpipe.core.util;
exports io.xpipe.core.data.node;
opens io.xpipe.core.data.node;

View file

@ -1,6 +1,6 @@
package io.xpipe.core.test;
import io.xpipe.core.data.DataStructureNode;
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;
@ -98,7 +98,7 @@ public class DataStructureTest {
var data = dataOut.toByteArray();
var reader = TypedDataStructureNodeReader.mutable(ds.type);
new TypedDataStreamParser(ds.type).readStructure(new ByteArrayInputStream(data), reader);
new TypedDataStreamParser(ds.type).parseStructure(new ByteArrayInputStream(data), reader);
var readNode = reader.create();
Assertions.assertEquals(node, readNode);
@ -125,7 +125,7 @@ public class DataStructureTest {
var data = dataOut.toByteArray();
var reader = TypedDataStructureNodeReader.immutable(ds.type);
new TypedDataStreamParser(ds.type).readStructure(new ByteArrayInputStream(data), reader);
new TypedDataStreamParser(ds.type).parseStructure(new ByteArrayInputStream(data), reader);
var readNode = reader.create();
Assertions.assertEquals(node, readNode);
@ -153,10 +153,10 @@ public class DataStructureTest {
var data = dataOut.toByteArray();
var in = new ByteArrayInputStream(data);
var reader = new TypedReusableDataStructureNodeReader(ds.type);
var reader = TypedReusableDataStructureNodeReader.create(ds.type);
for (var node : ds.nodes) {
new TypedDataStreamParser(ds.type).readStructure(in, reader);
new TypedDataStreamParser(ds.type).parseStructure(in, reader);
var readNode = reader.create();
Assertions.assertEquals(node, readNode);
}

View file

@ -1,6 +1,6 @@
package io.xpipe.core.test;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
@ -32,7 +32,10 @@ public class DataStructureTests {
DATA_5(createTestDataType5(), List.of(createTestData51(), createTestData52(), createTestData53())),
// Tuple with wildcard type
DATA_6(createTestDataType6(), List.of(createTestData61(), createTestData62()));
DATA_6(createTestDataType6(), List.of(createTestData61(), createTestData62())),
// Wildcard type
DATA_7(createTestDataType7(), List.of(createTestData71(), createTestData72(), createTestData73()));
public DataType type;
@ -85,7 +88,7 @@ public class DataStructureTests {
}
public static DataType createTestDataType2() {
return ArrayType.ofWildcard();
return ArrayType.of(WildcardType.of());
}
public static DataStructureNode createTestData31() {
@ -134,7 +137,7 @@ public class DataStructureTests {
}
public static DataType createTestDataType5() {
return ArrayType.ofWildcard();
return ArrayType.of(WildcardType.of());
}
public static DataStructureNode createTestData61() {
@ -157,4 +160,23 @@ public class DataStructureTests {
public static DataType createTestDataType6() {
return TupleType.of(List.of("key1", "key2"), List.of(WildcardType.of(), WildcardType.of()));
}
public static DataStructureNode createTestData71() {
return ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
}
public static DataStructureNode createTestData72() {
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
return TupleNode.builder().add("key1", val).build();
}
public static DataStructureNode createTestData73() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var array = ArrayNode.of(List.of(val, ValueNode.nullValue()));
return array;
}
public static DataType createTestDataType7() {
return WildcardType.of();
}
}

View file

@ -20,7 +20,7 @@ public interface DataSourceGuiProvider {
Region createConfigOptions(DataStore input, Property<? extends DataSourceDescriptor<?>> source);
DataSourceDescriptor<?> createDefaultDataSource(DataStore input);
DataSourceDescriptor<?> createDefaultDataSource(DataStore input) throws Exception;
String getDisplayName();