Rework everything

This commit is contained in:
Christopher Schnick 2021-12-15 01:50:00 +01:00
parent a523dd210b
commit 8a36027a31
62 changed files with 1272 additions and 535 deletions

View file

@ -4,26 +4,13 @@ plugins {
}
java {
modularity.inferModulePath = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
apply from: "$rootDir/deps/commons.gradle"
apply from: "$rootDir/deps/jackson.gradle"
dependencies {
implementation project(':core')
implementation project(':beacon')
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
//testImplementation project(':app')
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
maven { url "https://jitpack.io" }
}
plugins.withType(JavaPlugin).configureEach {
@ -32,8 +19,25 @@ plugins.withType(JavaPlugin).configureEach {
}
}
apply from: "$rootDir/deps/commons.gradle"
apply from: "$rootDir/deps/jackson.gradle"
dependencies {
implementation project(':core')
implementation project(':beacon')
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
testRuntimeOnly project(':app')
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
testRuntimeOnly "org.junit.platform:junit-platform-launcher"
}
test {
useJUnitPlatform()
testLogging {
exceptionFormat = 'full'
showStandardStreams = true
}
//workingDir = project(":app").projectDir
@ -41,4 +45,6 @@ test {
systemProperty "io.xpipe.daemon.mode", 'base'
systemProperty "io.xpipe.storage.dir", "$projectDir/test_env"
systemProperty "io.xpipe.beacon.port", "21722"
systemProperty 'io.xpipe.app.writeSysOut', "true"
systemProperty 'io.xpipe.app.logLevel', "debug"
}

View file

@ -1,14 +1,14 @@
package io.xpipe.api;
import io.xpipe.api.impl.DataTableImpl;
import io.xpipe.core.data.generic.ArrayNode;
import io.xpipe.core.data.generic.TupleNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.source.DataSourceId;
import java.util.OptionalInt;
public interface DataTable extends Iterable<TupleNode> {
public interface DataTable extends Iterable<SimpleTupleNode> {
static DataTable get(String s) {
return DataTableImpl.get(s);

View file

@ -10,13 +10,13 @@ public abstract class XPipeApiConnector extends BeaconConnector {
var socket = constructSocket();
handle(socket);
} catch (ConnectorException ce) {
throw new XPipeException("Connection error: " + ce.getMessage());
throw new XPipeConnectException(ce.getMessage());
} catch (ClientException ce) {
throw new XPipeException("Client error: " + ce.getMessage());
throw new XPipeClientException(ce.getMessage());
} catch (ServerException se) {
throw new XPipeException("Server error: " + se.getMessage());
throw new XPipeServerException(se.getMessage());
} catch (Throwable t) {
throw new XPipeException("Unexpected error", t);
throw new XPipeConnectException(t);
}
}

View file

@ -0,0 +1,19 @@
package io.xpipe.api;
public class XPipeClientException extends RuntimeException {
public XPipeClientException() {
}
public XPipeClientException(String message) {
super(message);
}
public XPipeClientException(String message, Throwable cause) {
super(message, cause);
}
public XPipeClientException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,19 @@
package io.xpipe.api;
public class XPipeConnectException extends RuntimeException {
public XPipeConnectException() {
}
public XPipeConnectException(String message) {
super(message);
}
public XPipeConnectException(String message, Throwable cause) {
super(message, cause);
}
public XPipeConnectException(Throwable cause) {
super(cause);
}
}

View file

@ -1,23 +0,0 @@
package io.xpipe.api;
public class XPipeException extends RuntimeException {
public XPipeException() {
}
public XPipeException(String message) {
super(message);
}
public XPipeException(String message, Throwable cause) {
super(message, cause);
}
public XPipeException(Throwable cause) {
super(cause);
}
public XPipeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View file

@ -0,0 +1,19 @@
package io.xpipe.api;
public class XPipeServerException extends RuntimeException {
public XPipeServerException() {
}
public XPipeServerException(String message) {
super(message);
}
public XPipeServerException(String message, Throwable cause) {
super(message, cause);
}
public XPipeServerException(Throwable cause) {
super(cause);
}
}

View file

@ -9,12 +9,12 @@ import io.xpipe.beacon.BeaconClient;
import io.xpipe.beacon.exchange.ReadTableDataExchange;
import io.xpipe.beacon.exchange.ReadTableInfoExchange;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.generic.ArrayNode;
import io.xpipe.core.data.generic.TupleNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.type.DataType;
import io.xpipe.core.data.type.TypedDataStreamReader;
import io.xpipe.core.data.type.callback.TypedDataStreamCallback;
import io.xpipe.core.data.type.callback.TypedDataStructureNodeCallback;
import io.xpipe.core.data.typed.TypedDataStreamReader;
import io.xpipe.core.data.typed.TypedDataStreamCallback;
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
import io.xpipe.core.source.DataSourceId;
import java.io.IOException;
@ -51,7 +51,7 @@ public class DataTableImpl implements DataTable {
this.dataType = dataType;
}
public Stream<TupleNode> stream() {
public Stream<SimpleTupleNode> stream() {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(iterator(), Spliterator.ORDERED), false);
}
@ -95,7 +95,7 @@ public class DataTableImpl implements DataTable {
protected void handle(BeaconClient sc) throws ClientException, ServerException, ConnectorException {
var req = new ReadTableDataExchange.Request(id, maxToRead);
performExchange(sc, req, (ReadTableDataExchange.Response res, InputStream in) -> {
TypedDataStreamReader.readStructures(in, new TypedDataStructureNodeCallback(dataType, nodes::add));
TypedDataStreamReader.readStructures(in, new TypedDataStructureNodeReader(dataType, nodes::add));
}, false);
}
}.execute();
@ -103,14 +103,14 @@ public class DataTableImpl implements DataTable {
}
@Override
public Iterator<TupleNode> iterator() {
return new Iterator<TupleNode>() {
public Iterator<SimpleTupleNode> iterator() {
return new Iterator<SimpleTupleNode>() {
private InputStream input;
private int read;
private final int toRead = size;
private TypedDataStreamCallback callback;
private TupleNode current;
private SimpleTupleNode current;
{
new XPipeApiConnector() {
@ -123,8 +123,8 @@ public class DataTableImpl implements DataTable {
}
}.execute();
callback = new TypedDataStructureNodeCallback(dataType, dsn -> {
current = (TupleNode) dsn;
callback = new TypedDataStructureNodeReader(dataType, dsn -> {
current = (SimpleTupleNode) dsn;
});
}
@ -150,7 +150,7 @@ public class DataTableImpl implements DataTable {
}
@Override
public TupleNode next() {
public SimpleTupleNode next() {
try {
TypedDataStreamReader.readStructure(input, callback);
} catch (IOException ex) {

View file

@ -1,6 +1,5 @@
package io.xpipe.api.test;
import io.xpipe.beacon.BeaconServer;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
@ -17,7 +16,7 @@ public class XPipeConfig implements BeforeAllCallback, ExtensionContext.Store.Cl
// Your "before all tests" startup logic goes here
// The following line registers a callback hook when the root test context is shut down
context.getRoot().getStore(GLOBAL).put("any unique name", this);
BeaconServer.start();
//BeaconServer.start();
}
}

View file

@ -15,3 +15,22 @@ repositories {
apply from: "$rootDir/deps/commons.gradle"
apply from: "$rootDir/deps/jackson.gradle"
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.8.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
testRuntimeOnly "org.junit.platform:junit-platform-launcher"
}
test {
useJUnitPlatform()
testLogging {
exceptionFormat = 'full'
showStandardStreams = true
}
}

View file

@ -12,10 +12,17 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
protected abstract String getName();
private UnsupportedOperationException unuspported(String s) {
protected UnsupportedOperationException unuspported(String s) {
return new UnsupportedOperationException(getName() + " does not support " + s);
}
@Override
public String toString() {
return toString(0);
}
public abstract String toString(int indent);
public boolean isTuple() {
return false;
}

View file

@ -0,0 +1,10 @@
package io.xpipe.core.data;
public class DataStructureNodeIO {
public static final int STRUCTURE_ID = 0;
public static final int TUPLE_ID = 1;
public static final int ARRAY_ID = 2;
public static final int VALUE_ID = 3;
public static final int NAME_ID = 4;
}

View file

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

View file

@ -1,55 +0,0 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import java.util.ArrayList;
import java.util.List;
public class ArrayReader implements DataStructureNodeReader {
private final List<DataStructureNode> nodes;
private int length;
private boolean hasSeenEnd;
private int currentIndex = 0;
private DataStructureNodeReader currentReader;
public ArrayReader(int length) {
this.length = length;
this.nodes = new ArrayList<>(length);
}
@Override
public void onArrayStart(String name, int length) {
DataStructureNodeReader.super.onArrayStart(name, length);
}
@Override
public void onArrayEnd() {
DataStructureNodeReader.super.onArrayEnd();
}
@Override
public void onTupleStart(String name, int length) {
DataStructureNodeReader.super.onTupleStart(name, length);
}
@Override
public void onTupleEnd() {
DataStructureNodeReader.super.onTupleEnd();
}
@Override
public void onValue(String name, byte[] value) {
DataStructureNodeReader.super.onValue(name, value);
}
@Override
public boolean isDone() {
return false;
}
@Override
public DataStructureNode create() {
return null;
}
}

View file

@ -1,30 +0,0 @@
package io.xpipe.core.data.generic;
import java.util.function.Consumer;
public interface DataStreamCallback {
static DataStreamCallback flat(Consumer<byte[]> con) {
return new DataStreamCallback() {
@Override
public void onValue(String name, byte[] value) {
con.accept(value);
}
};
}
default void onArrayStart(String name, int length) {
}
default void onArrayEnd() {
}
default void onTupleStart(String name, int length) {
}
default void onTupleEnd() {
}
default void onValue(String name, byte[] value) {
}
}

View file

@ -1,33 +0,0 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class DataStreamWriter {
private static final int TUPLE_ID = 1;
private static final int ARRAY_ID = 2;
private static final int VALUE_ID = 3;
public static void write(OutputStream out, DataStructureNode node) throws IOException {
if (node.isTuple()) {
writeTuple(out, (TupleNode) node);
}
}
private static void writeName(OutputStream out, String s) throws IOException {
out.write(s.length());
out.write(s.getBytes(StandardCharsets.UTF_8));
}
private static void writeTuple(OutputStream out, TupleNode tuple) throws IOException {
out.write(TUPLE_ID);
for (int i = 0; i < tuple.size(); i++) {
writeName(out, tuple.nameAt(i));
write(out, tuple.at(i));
}
}
}

View file

@ -1,69 +0,0 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
public class DataStructureReader implements DataStreamCallback {
private boolean isWrapped;
private DataStructureNodeReader reader;
public DataStructureNode create() {
return null;
}
@Override
public void onArrayStart(String name, int length) {
if (reader != null) {
reader.onArrayStart(name, length);
return;
}
if (name != null) {
reader = new TupleReader(1);
reader.onArrayStart(name, length);
} else {
reader = new ArrayReader(length);
reader.onArrayStart(null, length);
}
}
@Override
public void onArrayEnd() {
if (reader != null) {
reader.onArrayEnd();
}
}
@Override
public void onTupleStart(String name, int length) {
if (reader != null) {
reader.onTupleStart(name, length);
return;
}
if (name != null) {
reader = new TupleReader(1);
reader.onTupleStart(name, length);
} else {
reader = new TupleReader(length);
}
}
@Override
public void onTupleEnd() {
if (reader != null) {
reader.onTupleEnd();
if (reader.isDone()) {
}
}
DataStreamCallback.super.onTupleEnd();
}
@Override
public void onValue(String name, byte[] value) {
DataStreamCallback.super.onValue(name, value);
}
}

View file

@ -0,0 +1,161 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.ValueNode;
import java.util.ArrayList;
import java.util.List;
public class GenericArrayReader implements GenericDataStructureNodeReader {
public static GenericArrayReader newReader(int length) {
var ar = new GenericArrayReader();
ar.onArrayStart(length);
return ar;
}
private boolean initialized;
private List<DataStructureNode> nodes;
private int length;
private int currentIndex = 0;
private GenericDataStructureNodeReader currentReader;
private DataStructureNode created;
public GenericArrayReader() {
}
private void init(int length) {
this.length = length;
this.nodes = new ArrayList<>(length);
initialized = true;
}
private void put(DataStructureNode node) {
this.nodes.add(node);
currentIndex++;
}
@Override
public void onName(String name) {
if (hasReader()) {
currentReader.onName(name);
return;
}
throw new IllegalStateException("Expected array content but got a key name");
}
private boolean filled() {
return currentIndex == length;
}
private boolean isInitialized() {
return initialized;
}
private boolean hasReader() {
return currentReader != null;
}
@Override
public void onArrayStart(int length) {
if (hasReader()) {
currentReader.onArrayStart(length);
return;
}
if (!isInitialized()) {
init(length);
return;
}
currentReader = newReader(length);
}
@Override
public void onArrayEnd() {
if (hasReader()) {
currentReader.onArrayEnd();
if (currentReader.isDone()) {
put(currentReader.create());
currentReader = null;
}
return;
}
if (!isInitialized()) {
throw new IllegalStateException("Expected array start but got array end");
}
if (!filled()) {
throw new IllegalStateException("Array ended but is not full yet");
}
created = ArrayNode.wrap(nodes);
}
@Override
public void onTupleStart(int length) {
if (hasReader()) {
currentReader.onTupleStart(length);
return;
}
if (!isInitialized()) {
throw new IllegalStateException("Expected array start but got tuple start");
}
if (filled()) {
throw new IllegalStateException("Tuple is full but got another tuple");
}
currentReader = GenericTupleReader.newReader(length);
}
@Override
public void onTupleEnd() {
if (hasReader()) {
currentReader.onTupleEnd();
if (currentReader.isDone()) {
put(currentReader.create());
currentReader = null;
}
return;
}
throw new IllegalStateException("Expected array end but got tuple end");
}
@Override
public void onValue(byte[] value) {
if (currentReader != null) {
currentReader.onValue(value);
return;
}
if (!isInitialized()) {
throw new IllegalStateException("Expected array start but got value");
}
if (filled()) {
throw new IllegalStateException("Array is full but got another value");
}
put(ValueNode.wrap(value));
}
@Override
public boolean isDone() {
return filled() && created != null;
}
@Override
public DataStructureNode create() {
if (!isDone()) {
throw new IllegalStateException();
}
return ArrayNode.wrap(nodes);
}
}

View file

@ -0,0 +1,21 @@
package io.xpipe.core.data.generic;
public interface GenericDataStreamCallback {
default void onName(String name) {}
default void onArrayStart(int length) {
}
default void onArrayEnd() {
}
default void onTupleStart(int length) {
}
default void onTupleEnd() {
}
default void onValue(byte[] value) {
}
}

View file

@ -3,14 +3,19 @@ package io.xpipe.core.data.generic;
import java.io.IOException;
import java.io.InputStream;
public class DataStreamReader {
public class GenericDataStreamReader {
private static final int TUPLE_ID = 1;
private static final int ARRAY_ID = 2;
private static final int VALUE_ID = 3;
private static final int NAME_ID = 4;
public static void read(InputStream in, DataStreamCallback cb) throws IOException {
public static void read(InputStream in, GenericDataStreamCallback cb) throws IOException {
var b = in.read();
if (b == -1) {
return;
}
switch (b) {
case TUPLE_ID -> {
readTuple(in, cb);
@ -21,39 +26,41 @@ public class DataStreamReader {
case VALUE_ID -> {
readValue(in, cb);
}
case NAME_ID -> {
readName(in, cb);
read(in, cb);
}
default -> throw new IllegalStateException("Unexpected value: " + b);
}
}
private static String readName(InputStream in) throws IOException {
private static void readName(InputStream in, GenericDataStreamCallback cb) throws IOException {
var nameLength = in.read();
return new String(in.readNBytes(nameLength));
var name = new String(in.readNBytes(nameLength));
cb.onName(name);
}
private static void readTuple(InputStream in, DataStreamCallback cb) throws IOException {
var name = readName(in);
private static void readTuple(InputStream in, GenericDataStreamCallback cb) throws IOException {
var size = in.read();
cb.onTupleStart(name, size);
cb.onTupleStart(size);
for (int i = 0; i < size; i++) {
read(in, cb);
}
cb.onTupleEnd();
}
private static void readArray(InputStream in, DataStreamCallback cb) throws IOException {
var name = readName(in);
private static void readArray(InputStream in, GenericDataStreamCallback cb) throws IOException {
var size = in.read();
cb.onArrayStart(name, size);
cb.onArrayStart(size);
for (int i = 0; i < size; i++) {
read(in, cb);
}
cb.onArrayEnd();
}
private static void readValue(InputStream in, DataStreamCallback cb) throws IOException {
var name = readName(in);
private static void readValue(InputStream in, GenericDataStreamCallback cb) throws IOException {
var size = in.read();
var data = in.readNBytes(size);
cb.onValue(name, data);
cb.onValue(data);
}
}

View file

@ -0,0 +1,60 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class GenericDataStreamWriter {
private static final int TUPLE_ID = 1;
private static final int ARRAY_ID = 2;
private static final int VALUE_ID = 3;
private static final int NAME_ID = 4;
public static void write(OutputStream out, DataStructureNode node) throws IOException {
if (node.isTuple()) {
writeTuple(out, (TupleNode) node);
} else if (node.isArray()) {
writeArray(out, (ArrayNode) node);
} else if (node.isValue()) {
writeValue(out, (ValueNode) node);
} else {
throw new IllegalStateException();
}
}
private static void writeName(OutputStream out, String s) throws IOException {
var b = s.getBytes(StandardCharsets.UTF_8);
out.write(NAME_ID);
out.write(b.length);
out.write(b);
}
private static void writeTuple(OutputStream out, TupleNode tuple) throws IOException {
out.write(TUPLE_ID);
out.write(tuple.size());
for (int i = 0; i < tuple.size(); i++) {
writeName(out, tuple.nameAt(i));
write(out, tuple.at(i));
}
}
private static void writeArray(OutputStream out, ArrayNode array) throws IOException {
out.write(ARRAY_ID);
out.write(array.size());
for (int i = 0; i < array.size(); i++) {
write(out, array.at(i));
}
}
private static void writeValue(OutputStream out, ValueNode value) throws IOException {
out.write(VALUE_ID);
out.write(value.getRawData().length);
out.write(value.getRawData());
}
}

View file

@ -2,7 +2,7 @@ package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
public interface DataStructureNodeReader extends DataStreamCallback {
public interface GenericDataStructureNodeReader extends GenericDataStreamCallback {
boolean isDone();

View file

@ -0,0 +1,84 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.ValueNode;
public class GenericDataStructureReader implements GenericDataStreamCallback {
private DataStructureNode node;
private GenericDataStructureNodeReader reader;
public DataStructureNode create() {
if (node == null) {
throw new IllegalStateException("No node has been created yet");
}
return node;
}
private boolean hasReader() {
return reader != null;
}
@Override
public void onName(String name) {
if (hasReader()) {
reader.onName(name);
return;
}
throw new IllegalStateException("Expected node start but got name");
}
@Override
public void onArrayStart(int length) {
if (hasReader()) {
reader.onArrayStart(length);
return;
}
reader = GenericArrayReader.newReader(length);
}
@Override
public void onArrayEnd() {
if (!hasReader()) {
throw new IllegalStateException("No array to close");
}
reader.onArrayEnd();
}
@Override
public void onTupleStart(int length) {
if (hasReader()) {
reader.onTupleStart(length);
return;
}
reader = GenericTupleReader.newReader(length);
}
@Override
public void onTupleEnd() {
if (!hasReader()) {
throw new IllegalStateException("No tuple to close");
}
reader.onTupleEnd();
if (reader.isDone()) {
node = reader.create();
reader = null;
}
}
@Override
public void onValue(byte[] value) {
if (hasReader()) {
reader.onValue(value);
return;
}
node = ValueNode.wrap(value);
}
}

View file

@ -0,0 +1,168 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import java.util.ArrayList;
import java.util.List;
public class GenericTupleReader implements GenericDataStructureNodeReader {
public static GenericTupleReader newReader(int length) {
var tr = new GenericTupleReader();
tr.onTupleStart(length);
return tr;
}
private boolean initialized;
private int length;
private List<String> names;
private List<DataStructureNode> nodes;
private int currentIndex = 0;
private GenericDataStructureNodeReader currentReader;
private DataStructureNode created;
public GenericTupleReader() {
}
private boolean hasReader() {
return currentReader != null;
}
private void init(int length) {
this.length = length;
this.names = new ArrayList<>(length);
this.nodes = new ArrayList<>(length);
initialized = true;
}
private boolean isInitialized() {
return initialized;
}
private void putNode(DataStructureNode node) {
this.nodes.add(node);
currentIndex++;
}
private boolean filled() {
return currentIndex == length;
}
@Override
public void onName(String name) {
if (hasReader()) {
currentReader.onName(name);
return;
}
if (filled()) {
throw new IllegalStateException("Tuple is full but got another name");
}
if (names.size() - nodes.size() == 1) {
throw new IllegalStateException("Tuple is waiting for a node but got another name");
}
names.add(name);
}
@Override
public void onArrayStart(int length) {
if (hasReader()) {
currentReader.onArrayStart(length);
return;
}
if (!isInitialized()) {
throw new IllegalStateException("Expected tuple start but got array start");
}
currentReader = GenericArrayReader.newReader(length);
}
@Override
public void onArrayEnd() {
if (hasReader()) {
currentReader.onArrayEnd();
if (currentReader.isDone()) {
putNode(currentReader.create());
currentReader = null;
}
return;
}
throw new IllegalStateException("Expected tuple end but got array end");
}
@Override
public void onTupleStart(int length) {
if (hasReader()) {
currentReader.onTupleStart(length);
return;
}
if (!isInitialized()) {
init(length);
return;
}
currentReader = GenericTupleReader.newReader(length);
}
@Override
public void onTupleEnd() {
if (hasReader()) {
currentReader.onTupleEnd();
if (currentReader.isDone()) {
putNode(currentReader.create());
currentReader = null;
}
return;
}
if (!isInitialized()) {
throw new IllegalStateException("Expected tuple start but got tuple end");
}
if (!filled()) {
throw new IllegalStateException("Tuple ended but is not full yet");
}
created = TupleNode.wrap(names, nodes);
}
@Override
public void onValue(byte[] value) {
if (currentReader != null) {
currentReader.onValue(value);
return;
}
if (!isInitialized()) {
throw new IllegalStateException("Expected tuple start but got value");
}
if (filled()) {
throw new IllegalStateException("Tuple is full but got another value");
}
putNode(ValueNode.wrap(value));
}
@Override
public boolean isDone() {
return filled() && created != null;
}
@Override
public DataStructureNode create() {
if (!isDone()) {
throw new IllegalStateException();
}
return SimpleTupleNode.wrap(names, nodes);
}
}

View file

@ -1,119 +0,0 @@
package io.xpipe.core.data.generic;
import io.xpipe.core.data.DataStructureNode;
import java.util.ArrayList;
import java.util.List;
public class TupleReader implements DataStructureNodeReader {
private final int length;
private final List<String> names;
private final List<DataStructureNode> nodes;
private boolean hasSeenEnd;
private int currentIndex = 0;
private DataStructureNodeReader currentReader;
public TupleReader(int length) {
this.length = length;
this.names = new ArrayList<>(length);
this.nodes = new ArrayList<>(length);
}
private void put(String name, DataStructureNode node) {
this.names.add(name);
this.nodes.add(node);
currentIndex++;
}
private void putNode(DataStructureNode node) {
this.nodes.add(node);
currentIndex++;
}
private boolean filled() {
return currentIndex == length;
}
@Override
public void onArrayStart(String name, int length) {
if (currentReader != null) {
currentReader.onArrayStart(name, length);
return;
}
names.add(name);
currentReader = new ArrayReader(length);
}
@Override
public void onArrayEnd() {
if (currentReader != null) {
currentReader.onArrayEnd();
if (currentReader.isDone()) {
putNode(currentReader.create());
currentReader = null;
}
return;
}
throw new IllegalStateException();
}
@Override
public void onTupleStart(String name, int length) {
if (currentReader != null) {
currentReader.onTupleStart(name, length);
return;
}
names.add(name);
currentReader = new TupleReader(length);
}
@Override
public void onTupleEnd() {
if (currentReader != null) {
currentReader.onTupleEnd();
if (currentReader.isDone()) {
putNode(currentReader.create());
currentReader = null;
}
return;
}
if (!filled()) {
throw new IllegalStateException();
}
hasSeenEnd = true;
}
@Override
public void onValue(String name, byte[] value) {
if (currentReader != null) {
currentReader.onValue(name, value);
return;
}
if (filled()) {
throw new IllegalStateException();
}
put(name, ValueNode.wrap(value));
}
@Override
public boolean isDone() {
return filled() && hasSeenEnd;
}
@Override
public DataStructureNode create() {
if (!isDone()) {
throw new IllegalStateException();
}
return TupleNode.wrap(names, nodes);
}
}

View file

@ -1,13 +1,16 @@
package io.xpipe.core.data.generic;
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.type.ArrayType;
import io.xpipe.core.data.type.DataType;
import lombok.EqualsAndHashCode;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@EqualsAndHashCode(callSuper = false)
public class ArrayNode extends DataStructureNode {
private final List<DataStructureNode> valueNodes;
@ -16,6 +19,10 @@ public class ArrayNode extends DataStructureNode {
this.valueNodes = valueNodes;
}
public static ArrayNode of(DataStructureNode... dsn) {
return wrap(List.of(dsn));
}
public static ArrayNode wrap(List<DataStructureNode> valueNodes) {
return new ArrayNode(valueNodes);
}
@ -44,6 +51,12 @@ public class ArrayNode extends DataStructureNode {
return "array node";
}
@Override
public String toString(int indent) {
var content = valueNodes.stream().map(n -> n.toString(indent)).collect(Collectors.joining(", "));
return "[" + content + "]";
}
@Override
public DataType getDataType() {
return ArrayType.of(valueNodes.stream().map(DataStructureNode::getDataType).toList());

View file

@ -0,0 +1,65 @@
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 java.util.Collections;
import java.util.List;
import java.util.Optional;
public class NoKeyTupleNode extends TupleNode {
private final List<DataStructureNode> nodes;
NoKeyTupleNode(List<DataStructureNode> nodes) {
this.nodes = nodes;
}
@Override
public DataType getDataType() {
return TupleType.wrap(null, nodes.stream().map(DataStructureNode::getDataType).toList());
}
@Override
protected String getName() {
return "no key tuple node";
}
@Override
public DataStructureNode at(int index) {
return nodes.get(index);
}
@Override
public DataStructureNode forKey(String name) {
throw unuspported("key indexing");
}
@Override
public Optional<DataStructureNode> forKeyIfPresent(String name) {
return Optional.empty();
}
@Override
public int size() {
return nodes.size();
}
public String nameAt(int index) {
throw unuspported("name getter");
}
@Override
public List<KeyValue> getKeyValuePairs() {
return nodes.stream().map(n -> new KeyValue(null, n)).toList();
}
public List<String> getNames() {
return Collections.nCopies(size(), null);
}
public List<DataStructureNode> getNodes() {
return Collections.unmodifiableList(nodes);
}
}

View file

@ -1,36 +1,23 @@
package io.xpipe.core.data.generic;
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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.*;
public class TupleNode extends DataStructureNode {
@EqualsAndHashCode(callSuper = false)
public class SimpleTupleNode extends TupleNode {
private final List<String> names;
private final List<DataStructureNode> nodes;
private TupleNode(List<String> names, List<DataStructureNode> nodes) {
SimpleTupleNode(List<String> names, List<DataStructureNode> nodes) {
this.names = names;
this.nodes = nodes;
}
public static TupleNode wrap(List<String> names, List<DataStructureNode> nodes) {
return new TupleNode(names, nodes);
}
public static TupleNode copy(List<String> names, List<DataStructureNode> nodes) {
return new TupleNode(new ArrayList<>(names), new ArrayList<>(nodes));
}
public boolean isTuple() {
return true;
}
@Override
public DataType getDataType() {
return TupleType.wrap(names, nodes.stream().map(DataStructureNode::getDataType).toList());
@ -69,6 +56,15 @@ public class TupleNode extends DataStructureNode {
return names.get(index);
}
@Override
public List<KeyValue> getKeyValuePairs() {
var l = new ArrayList<KeyValue>(size());
for (int i = 0; i < size(); i++) {
l.add(new KeyValue(getNames().get(i), getNodes().get(i)));
}
return l;
}
public List<String> getNames() {
return Collections.unmodifiableList(names);
}

View file

@ -0,0 +1,115 @@
package io.xpipe.core.data.node;
import io.xpipe.core.data.DataStructureNode;
import lombok.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public abstract class TupleNode extends DataStructureNode {
@Value
public static class KeyValue {
String key;
DataStructureNode value;
}
public static class Builder {
private final List<KeyValue> entries = new ArrayList<>();
public Builder add(String name, DataStructureNode node) {
Objects.requireNonNull(node);
entries.add(new KeyValue(name, node));
return this;
}
public Builder add(DataStructureNode node) {
Objects.requireNonNull(node);
entries.add(new KeyValue(null, node));
return this;
}
public TupleNode build() {
boolean hasKeys = entries.stream().anyMatch(kv -> kv.key != null);
return hasKeys ? TupleNode.wrap(
entries.stream().map(kv -> kv.key).toList(),
entries.stream().map(kv -> kv.value).toList()) :
TupleNode.wrap(entries.stream().map(kv -> kv.value).toList());
}
}
public static Builder builder() {
return new Builder();
}
public static TupleNode wrap(List<DataStructureNode> nodes) {
if (nodes == null) {
throw new IllegalArgumentException("Nodes must be not null");
}
return new NoKeyTupleNode(nodes);
}
public static TupleNode copy(List<DataStructureNode> nodes) {
return TupleNode.wrap(List.copyOf(nodes));
}
public static TupleNode wrap(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");
}
if (names.size() != nodes.size()) {
throw new IllegalArgumentException("Names and nodes must have the same length");
}
return new SimpleTupleNode(names, nodes);
}
public static TupleNode wrapRaw(List<String> names, List<DataStructureNode> nodes) {
if (names == null) {
throw new IllegalArgumentException("Names must be not null");
}
if (nodes == null) {
throw new IllegalArgumentException("Nodes must be not null");
}
return new SimpleTupleNode(names, nodes);
}
public static TupleNode copy(List<String> names, List<DataStructureNode> nodes) {
return TupleNode.wrap(List.copyOf(names), List.copyOf(nodes));
}
public final boolean isTuple() {
return true;
}
@Override
public String toString(int indent) {
var is = " ".repeat(indent);
var start = getClass().getSimpleName() + " {\n";
var kvs = getKeyValuePairs().stream().map(kv -> {
if (kv.key == null) {
return is + " " + kv.value.toString(indent + 1) + "\n";
} else {
return is + " " + kv.key + "=" + kv.value.toString(indent + 1) + "\n";
}
}).collect(Collectors.joining());
var end = is + "}";
return start + kvs + end;
}
public abstract String nameAt(int index);
public abstract List<KeyValue> getKeyValuePairs();
public abstract List<String> getNames();
public abstract List<DataStructureNode> getNodes();
}

View file

@ -1,9 +1,13 @@
package io.xpipe.core.data.generic;
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;
import java.nio.charset.StandardCharsets;
@EqualsAndHashCode(callSuper = false)
public class ValueNode extends DataStructureNode {
private final byte[] data;
@ -16,6 +20,10 @@ public class ValueNode extends DataStructureNode {
return new ValueNode(data);
}
public static ValueNode of(Object o) {
return new ValueNode(o.toString().getBytes(StandardCharsets.UTF_8));
}
@Override
public boolean isValue() {
return true;
@ -36,6 +44,11 @@ public class ValueNode extends DataStructureNode {
return "value node";
}
@Override
public String toString(int indent) {
return new String(data);
}
@Override
public DataType getDataType() {
return new ValueType();

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.type.callback;
import io.xpipe.core.data.generic.DataStructureNodePointer;
import io.xpipe.core.data.DataStructureNodePointer;
import io.xpipe.core.data.type.ArrayType;
import io.xpipe.core.data.type.TupleType;

View file

@ -1,6 +1,6 @@
package io.xpipe.core.data.type.callback;
import io.xpipe.core.data.generic.DataStructureNodePointer;
import io.xpipe.core.data.DataStructureNodePointer;
import io.xpipe.core.data.type.ArrayType;
import io.xpipe.core.data.type.TupleType;

View file

@ -1,4 +1,4 @@
package io.xpipe.core.data.type.callback;
package io.xpipe.core.data.typed;
public class ReusableTypedDataStructureNodeCallback implements TypedDataStreamCallback {

View file

@ -1,4 +1,4 @@
package io.xpipe.core.data.type.callback;
package io.xpipe.core.data.typed;
public interface TypedDataStreamCallback {

View file

@ -1,6 +1,4 @@
package io.xpipe.core.data.type;
import io.xpipe.core.data.type.callback.TypedDataStreamCallback;
package io.xpipe.core.data.typed;
import java.io.IOException;
import java.io.InputStream;
@ -19,18 +17,14 @@ public class TypedDataStreamReader {
}
if (b != STRUCTURE_ID) {
throw new IOException("Unexpected value: " + b);
throw new IllegalStateException("Unexpected value: " + b);
}
return true;
}
public static void readStructures(InputStream in, TypedDataStreamCallback cb) throws IOException {
while (true) {
if (!hasNext(in)) {
break;
}
while (hasNext(in)) {
cb.onNodeBegin();
read(in, cb);
cb.onNodeEnd();

View file

@ -1,9 +1,9 @@
package io.xpipe.core.data.type;
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.generic.ArrayNode;
import io.xpipe.core.data.generic.TupleNode;
import io.xpipe.core.data.generic.ValueNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.node.ValueNode;
import java.io.IOException;
import java.io.OutputStream;
@ -22,7 +22,7 @@ public class TypedDataStreamWriter {
private static void write(OutputStream out, DataStructureNode node) throws IOException {
if (node.isTuple()) {
writeTuple(out, (TupleNode) node);
writeTuple(out, (SimpleTupleNode) node);
}
else if (node.isArray()) {
writeArray(out, (ArrayNode) node);
@ -40,7 +40,7 @@ public class TypedDataStreamWriter {
out.write(n.getRawData());
}
private static void writeTuple(OutputStream out, TupleNode tuple) throws IOException {
private static void writeTuple(OutputStream out, SimpleTupleNode tuple) throws IOException {
out.write(TUPLE_ID);
out.write(tuple.size());
for (int i = 0; i < tuple.size(); i++) {

View file

@ -1,28 +1,29 @@
package io.xpipe.core.data.type.callback;
package io.xpipe.core.data.typed;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.generic.ArrayNode;
import io.xpipe.core.data.generic.TupleNode;
import io.xpipe.core.data.generic.ValueNode;
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 java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.function.Consumer;
public class TypedDataStructureNodeCallback implements TypedDataStreamCallback {
public class TypedDataStructureNodeReader implements TypedDataStreamCallback {
private int currentDataTypeIndex;
private final List<DataType> flattened;
private int dataTypeIndex;
private Stack<List<DataStructureNode>> children;
private Stack<DataStructureNode> nodes;
private DataStructureNode readNode;
private final Consumer<DataStructureNode> consumer;
private boolean initialized;
private int arrayDepth;
public TypedDataStructureNodeCallback(DataType type, Consumer<DataStructureNode> consumer) {
this.consumer = consumer;
public TypedDataStructureNodeReader(DataType type) {
flattened = new ArrayList<>();
children = new Stack<>();
nodes = new Stack<>();
@ -35,51 +36,43 @@ public class TypedDataStructureNodeCallback implements TypedDataStreamCallback {
throw new IllegalStateException();
}
dataTypeIndex = 0;
readNode = null;
}
public DataStructureNode create() {
return readNode;
}
@Override
public void onNodeEnd() {
if (nodes.size() != 0 || children.size() != 0 || readNode == null) {
throw new IllegalStateException();
}
consumer.accept(readNode);
initialized = false;
}
@Override
public void onValue(byte[] data) {
children.peek().add(ValueNode.wrap(data));
if (!flattened.get(dataTypeIndex).isArray()) {
dataTypeIndex++;
if (!initialized) {
readNode = ValueNode.wrap(data);
return;
}
}
protected void newTuple() {
TupleType tupleType = (TupleType) flattened.get(dataTypeIndex);
var l = new ArrayList<DataStructureNode>(tupleType.getSize());
children.push(l);
var newNode = TupleNode.wrap(tupleType.getNames(), l);
nodes.push(newNode);
}
protected void newArray() {
var l = new ArrayList<DataStructureNode>();
children.push(new ArrayList<>());
var newNode = ArrayNode.wrap(l);
nodes.push(newNode);
children.peek().add(ValueNode.wrap(data));
if (!flattened.get(currentDataTypeIndex).isArray()) {
currentDataTypeIndex++;
}
}
private void finishTuple() {
children.pop();
dataTypeIndex++;
var popped = nodes.pop();
if (!popped.isTuple()) {
throw new IllegalStateException();
}
TupleNode tuple = (TupleNode) popped;
SimpleTupleNode tuple = (SimpleTupleNode) popped;
if (tuple.getNames().size() != tuple.getNodes().size()) {
throw new IllegalStateException("");
}
@ -91,9 +84,17 @@ public class TypedDataStructureNodeCallback implements TypedDataStreamCallback {
}
}
private boolean isInArray() {
return arrayDepth >= 1;
}
private void finishArray() {
arrayDepth--;
if (!isInArray()) {
currentDataTypeIndex++;
}
children.pop();
dataTypeIndex++;
var popped = nodes.pop();
if (nodes.empty()) {
readNode = popped;
@ -104,11 +105,26 @@ public class TypedDataStructureNodeCallback implements TypedDataStreamCallback {
@Override
public void onTupleBegin(int size) {
if (!flattened.get(dataTypeIndex).isTuple()) {
if (flattened.size() == currentDataTypeIndex) {
int a = 0;
}
if (!isInArray() && !flattened.get(currentDataTypeIndex).isTuple()) {
throw new IllegalStateException();
}
newTuple();
TupleType tupleType = (TupleType) flattened.get(currentDataTypeIndex);
if (!initialized || !flattened.get(currentDataTypeIndex).isArray()) {
currentDataTypeIndex++;
}
if (!initialized) {
initialized = true;
}
var l = new ArrayList<DataStructureNode>(tupleType.getSize());
children.push(l);
var newNode = TupleNode.wrapRaw(tupleType.getNames(), l);
nodes.push(newNode);
}
@Override
@ -118,11 +134,15 @@ public class TypedDataStructureNodeCallback implements TypedDataStreamCallback {
@Override
public void onArrayBegin(int size) {
if (!flattened.get(dataTypeIndex).isArray()) {
if (!flattened.get(currentDataTypeIndex).isArray()) {
throw new IllegalStateException();
}
newArray();
var l = new ArrayList<DataStructureNode>();
children.push(l);
var newNode = ArrayNode.wrap(l);
nodes.push(newNode);
arrayDepth++;
}
@Override

View file

@ -4,7 +4,7 @@ import io.xpipe.core.store.DataStore;
import java.util.Optional;
public interface DataSource<DS extends DataStore> {
public interface DataSourceDescriptor<DS extends DataStore> {
default Optional<String> determineDefaultName(DS store) {
return Optional.empty();

View file

@ -1,15 +1,40 @@
package io.xpipe.core.source;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.EqualsAndHashCode;
import lombok.Getter;
/**
* Represents a reference to an XPipe data source.
* This reference consists out of a collection name and an entry name to allow for better organisation.
*
* To allow for a simple usage of data source ids, the collection and entry names are trimmed and
* converted to lower case names when creating them.
* The two names are separated by a colon and are therefore not allowed to contain colons themselves.
*
* @see #create(String, String)
* @see #fromString(String)
*/
@EqualsAndHashCode
@Getter
public class DataSourceId {
public static final char SEPARATOR = ':';
/**
* Creates a new data source id from a collection name and an entry name.
*
* @param collectionName the collection name, which must be not null and not empty
* @param entryName the entry name, which must be not null and not empty
* @throws IllegalArgumentException if any name is not valid
*/
public static DataSourceId create(String collectionName, String entryName) {
if (collectionName == null) {
throw new IllegalArgumentException("Collection name is null");
}
if (collectionName.trim().length() == 0) {
throw new IllegalArgumentException("Trimmed collection name is empty");
}
if (collectionName.contains("" + SEPARATOR)) {
throw new IllegalArgumentException("Separator character " + SEPARATOR + " is not allowed in the collection name");
}
@ -17,6 +42,9 @@ public class DataSourceId {
if (entryName == null) {
throw new IllegalArgumentException("Collection name is null");
}
if (entryName.trim().length() == 0) {
throw new IllegalArgumentException("Trimmed entry name is empty");
}
if (entryName.contains("" + SEPARATOR)) {
throw new IllegalArgumentException("Separator character " + SEPARATOR + " is not allowed in the entry name");
}
@ -33,6 +61,13 @@ public class DataSourceId {
this.entryName = entryName;
}
/**
* Creates a new data source id from a string representation.
* The string must contain exactly one colon and non-empty names.
*
* @param s the string representation, must be not null and fulfill certain requirements
* @throws IllegalArgumentException if the string is not valid
*/
public static DataSourceId fromString(String s) {
if (s == null) {
throw new IllegalArgumentException("String is null");
@ -43,30 +78,20 @@ public class DataSourceId {
throw new IllegalArgumentException("Data source id must contain exactly one " + SEPARATOR);
}
if (split[0].length() == 0) {
var cn = split[0].trim().toLowerCase();
var en = split[1].trim().toLowerCase();
if (cn.length() == 0) {
throw new IllegalArgumentException("Collection name must not be empty");
}
if (split[1].length() == 0) {
if (en.length() == 0) {
throw new IllegalArgumentException("Entry name must not be empty");
}
return new DataSourceId(split[0], split[1]);
return new DataSourceId(cn, en);
}
@Override
public String toString() {
return (collectionName != null ? collectionName : "") + SEPARATOR + entryName;
}
public String toReferenceValue() {
return toString().toLowerCase();
}
public String getCollectionName() {
return collectionName;
}
public String getEntryName() {
return entryName;
return collectionName + SEPARATOR + entryName;
}
}

View file

@ -8,5 +8,8 @@ public enum DataSourceType {
TABLE,
@JsonProperty("structure")
STRUCTURE
STRUCTURE,
@JsonProperty("raw")
RAW
}

View file

@ -1,5 +0,0 @@
package io.xpipe.core.source;
public interface DataStructureConnection extends DataSourceConnection {
}

View file

@ -1,15 +0,0 @@
package io.xpipe.core.source;
import io.xpipe.core.store.DataStore;
public abstract class DataTableSource<DS extends DataStore> implements DataSource<DS> {
public abstract DataTableWriteConnection openWriteConnection(DS store);
public abstract DataTableConnection openConnection(DS store);
@Override
public DataSourceType getType() {
return DataSourceType.TABLE;
}
}

View file

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

View file

@ -0,0 +1,5 @@
package io.xpipe.core.source;
public interface StructureDataReadConnection extends DataSourceConnection {
}

View file

@ -1,6 +1,6 @@
package io.xpipe.core.source;
public abstract class DataStructureSource implements DataSource {
public abstract class StructureDataSourceDescriptor implements DataSourceDescriptor {
public abstract DataSourceConnection openConnection() throws Exception;

View file

@ -2,19 +2,19 @@ package io.xpipe.core.source;
import io.xpipe.core.data.DataStructureNodeAcceptor;
import io.xpipe.core.data.generic.ArrayNode;
import io.xpipe.core.data.generic.TupleNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.SimpleTupleNode;
import io.xpipe.core.data.type.TupleType;
import java.io.OutputStream;
public interface DataTableConnection extends DataSourceConnection {
public interface TableDataReadConnection extends DataSourceConnection {
TupleType determineDataType() throws Exception;
int determineRowCount() throws Exception;
void withLines(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception;
void withLines(DataStructureNodeAcceptor<SimpleTupleNode> lineAcceptor) throws Exception;
ArrayNode readLines(int maxLines) throws Exception;

View file

@ -0,0 +1,15 @@
package io.xpipe.core.source;
import io.xpipe.core.store.DataStore;
public abstract class TableDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> {
public abstract TableDataWriteConnection openWriteConnection(DS store);
public abstract TableDataReadConnection openConnection(DS store);
@Override
public DataSourceType getType() {
return DataSourceType.TABLE;
}
}

View file

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

View file

@ -2,7 +2,7 @@ package io.xpipe.core.store;
import com.fasterxml.jackson.annotation.JsonIgnore;
public abstract class FileDataInput implements StreamDataStore {
public abstract class FileDataStore implements StreamDataStore {
public abstract String getName();
@ -10,8 +10,8 @@ public abstract class FileDataInput implements StreamDataStore {
public abstract boolean isLocal();
@JsonIgnore
public abstract LocalFileDataInput getLocal();
public abstract LocalFileDataStore getLocal();
@JsonIgnore
public abstract RemoteFileDataInput getRemote();
public abstract RemoteFileDataStore getRemote();
}

View file

@ -12,12 +12,12 @@ import java.time.Instant;
import java.util.Optional;
@JsonTypeName("local")
public class LocalFileDataInput extends FileDataInput {
public class LocalFileDataStore extends FileDataStore {
private final Path file;
@JsonCreator
public LocalFileDataInput(Path file) {
public LocalFileDataStore(Path file) {
this.file = file;
}
@ -48,12 +48,12 @@ public class LocalFileDataInput extends FileDataInput {
}
@Override
public LocalFileDataInput getLocal() {
public LocalFileDataStore getLocal() {
return this;
}
@Override
public RemoteFileDataInput getRemote() {
public RemoteFileDataStore getRemote() {
throw new UnsupportedOperationException();
}

View file

@ -5,7 +5,7 @@ import java.io.OutputStream;
import java.time.Instant;
import java.util.Optional;
public class RemoteFileDataInput extends FileDataInput {
public class RemoteFileDataStore extends FileDataStore {
@Override
public Optional<String> determineDefaultName() {
@ -28,12 +28,12 @@ public class RemoteFileDataInput extends FileDataInput {
}
@Override
public LocalFileDataInput getLocal() {
public LocalFileDataStore getLocal() {
return null;
}
@Override
public RemoteFileDataInput getRemote() {
public RemoteFileDataStore getRemote() {
return null;
}

View file

@ -12,7 +12,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
import io.xpipe.core.data.type.ArrayType;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.type.ValueType;
import io.xpipe.core.store.LocalFileDataInput;
import io.xpipe.core.store.LocalFileDataStore;
import java.io.IOException;
import java.nio.charset.Charset;
@ -57,7 +57,7 @@ public class CoreJacksonModule extends SimpleModule {
@Override
public void setupModule(SetupContext context) {
context.registerSubtypes(
new NamedType(LocalFileDataInput.class),
new NamedType(LocalFileDataStore.class),
new NamedType(ValueType.class),
new NamedType(TupleType.class),
new NamedType(ArrayType.class)

View file

@ -5,6 +5,8 @@ module io.xpipe.core {
requires transitive com.fasterxml.jackson.databind;
requires transitive com.fasterxml.jackson.module.paramnames;
requires static lombok;
exports io.xpipe.core.store;
exports io.xpipe.core.source;
exports io.xpipe.core.data.generic;
@ -19,6 +21,10 @@ module io.xpipe.core {
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;
exports io.xpipe.core.data.typed;
opens io.xpipe.core.data.typed;
uses com.fasterxml.jackson.databind.Module;
provides com.fasterxml.jackson.databind.Module with CoreJacksonModule;

View file

@ -0,0 +1,62 @@
package io.xpipe.core.test;
import io.xpipe.core.source.DataSourceId;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
public class DataSourceIdTest {
@Test
public void testCreateInvalidParameters() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create(null, "abc");
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create("a:bc", "abc");
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create(" \t", "abc");
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create("", "abc");
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create("abc", null);
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create("abc", "a:bc");
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create("abc", " \t");
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.create("abc", "");
});
}
@Test
public void testFromStringNullParameters() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.fromString(null);
});
}
@ParameterizedTest
@ValueSource(strings = {"abc", "abc:", "ab::c", ":abc", " :ab", "::::", "", " "})
public void testFromStringInvalidParameters(String arg) {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
DataSourceId.fromString(arg);
});
}
@Test
public void testFromStringValidParameters() {
Assertions.assertEquals(DataSourceId.fromString("ab:c"), DataSourceId.fromString(" ab: c "));
Assertions.assertEquals(DataSourceId.fromString("ab:c"), DataSourceId.fromString(" AB: C "));
Assertions.assertEquals(DataSourceId.fromString("ab:c"), DataSourceId.fromString("ab:c "));
}
}

View file

@ -0,0 +1,56 @@
package io.xpipe.core.test;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DataStructureTest {
public static DataStructureNode createTestData() {
var val = ValueNode.wrap("value".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.wrap(List.of(ValueNode.of(1), ValueNode.of(2)));
var flatTuple = TupleNode.builder().add("key1", val).build();
var nestedArray = ArrayNode.wrap(List.of(flatArray, flatTuple));
return TupleNode.builder()
.add("key1", val)
.add("key2", flatArray)
.add("key3", flatTuple)
.add("key4", nestedArray)
.build();
}
@Test
public void testBasicOperations() {
var obj = createTestData();
Assertions.assertEquals(obj.size(), 4);
Assertions.assertTrue(obj.isTuple());
var objCopy = createTestData();
Assertions.assertEquals(obj, objCopy);
var key1 = obj.forKey("key1").asString();
Assertions.assertEquals(key1, "value");
var key2 = obj.forKey("key2");
Assertions.assertTrue(key2.isArray());
Assertions.assertEquals(key2.at(0), ValueNode.of(1));
Assertions.assertEquals(key2.at(0).asString(), "1");
Assertions.assertEquals(key2.at(0).asInt(), 1);
var key3 = obj.forKey("key3");
Assertions.assertTrue(key3.isTuple());
Assertions.assertEquals(key3.forKey("key1"), ValueNode.of("value"));
Assertions.assertEquals(key3.forKey("key1").asString(), "value");
var key4 = obj.forKey("key4");
Assertions.assertTrue(key4.isArray());
Assertions.assertEquals(key4.at(0), ArrayNode.of(ValueNode.of(1), ValueNode.of(2)));
Assertions.assertEquals(key4.at(0).at(0).asInt(), 1);
}
}

View file

@ -0,0 +1,25 @@
package io.xpipe.core.test;
import io.xpipe.core.data.DataStructureNode;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DataStructureTests {
public static DataStructureNode createTestData() {
var val = ValueNode.wrap("value".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.wrap(List.of(ValueNode.of(1), ValueNode.of(2)));
var flatTuple = TupleNode.builder().add("key1", val).build();
var nestedArray = ArrayNode.wrap(List.of(flatArray, flatTuple));
return TupleNode.builder()
.add("key1", val)
.add("key2", flatArray)
.add("key3", flatTuple)
.add("key4", nestedArray)
.build();
}
}

View file

@ -0,0 +1,35 @@
package io.xpipe.core.test;
import io.xpipe.core.data.generic.GenericDataStreamReader;
import io.xpipe.core.data.generic.GenericDataStreamWriter;
import io.xpipe.core.data.generic.GenericDataStructureReader;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HexFormat;
import static io.xpipe.core.test.DataStructureTests.createTestData;
public class GenericDataStructureIoTest {
@Test
public void testBasicIo() throws IOException {
var obj = createTestData();
var dataOut = new ByteArrayOutputStream();
GenericDataStreamWriter.write(dataOut, obj);
var data = dataOut.toByteArray();
var format = HexFormat.of().withPrefix("0x").withDelimiter(" ");
System.out.println(format.formatHex(data));
var reader = new GenericDataStructureReader();
GenericDataStreamReader.read(new ByteArrayInputStream(data), reader);
var node = reader.create();
Assertions.assertEquals(obj, node);
System.out.println(node);
}
}

View file

@ -0,0 +1,36 @@
package io.xpipe.core.test;
import io.xpipe.core.data.typed.TypedDataStreamReader;
import io.xpipe.core.data.typed.TypedDataStreamWriter;
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HexFormat;
import static io.xpipe.core.test.DataStructureTests.createTestData;
public class TypedDataStructureIoTest {
@Test
public void testBasicIo() throws IOException {
var obj = createTestData();
var type = obj.getDataType();
var dataOut = new ByteArrayOutputStream();
TypedDataStreamWriter.writeStructure(dataOut, obj);
var data = dataOut.toByteArray();
var format = HexFormat.of().withPrefix("0x").withDelimiter(" ");
System.out.println(format.formatHex(data));
var reader = new TypedDataStructureNodeReader(type);
TypedDataStreamReader.readStructure(new ByteArrayInputStream(data), reader);
var node = reader.create();
Assertions.assertEquals(obj, node);
System.out.println(node);
}
}

View file

@ -0,0 +1,7 @@
module io.xpipe.core.test {
exports io.xpipe.core.test;
requires org.junit.jupiter.api;
requires org.junit.jupiter.params;
requires io.xpipe.core;
}

View file

@ -1,6 +1,6 @@
package io.xpipe.extension;
import io.xpipe.core.source.DataSource;
import io.xpipe.core.source.DataSourceDescriptor;
import io.xpipe.core.store.DataStore;
import javafx.beans.property.Property;
import javafx.scene.image.Image;
@ -18,9 +18,9 @@ public interface DataSourceGuiProvider {
boolean supportsFile(Path file);
Region createConfigOptions(DataStore input, Property<? extends DataSource<?>> source);
Region createConfigOptions(DataStore input, Property<? extends DataSourceDescriptor<?>> source);
DataSource<?> createDefaultDataSource(DataStore input);
DataSourceDescriptor<?> createDefaultDataSource(DataStore input);
String getDisplayName();
@ -30,7 +30,7 @@ public interface DataSourceGuiProvider {
Map<Supplier<String>, String> getFileExtensions();
String getDataSourceDescription(DataSource<?> source);
String getDataSourceDescription(DataSourceDescriptor<?> source);
Class<? extends DataSource<?>> getType();
Class<? extends DataSourceDescriptor<?>> getType();
}

View file

@ -1,10 +1,10 @@
package io.xpipe.extension;
import io.xpipe.core.source.DataSource;
import io.xpipe.core.source.DataSourceDescriptor;
public interface DataSourceProvider {
String getId();
Class<? extends DataSource<?>> getType();
Class<? extends DataSourceDescriptor<?>> getType();
}

View file

@ -1,7 +1,5 @@
package io.xpipe.extension;
import io.xpipe.core.source.DataSource;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;