mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 00:50:31 +00:00
Rework everything
This commit is contained in:
parent
a523dd210b
commit
8a36027a31
62 changed files with 1272 additions and 535 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
19
api/src/main/java/io/xpipe/api/XPipeClientException.java
Normal file
19
api/src/main/java/io/xpipe/api/XPipeClientException.java
Normal 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);
|
||||
}
|
||||
}
|
19
api/src/main/java/io/xpipe/api/XPipeConnectException.java
Normal file
19
api/src/main/java/io/xpipe/api/XPipeConnectException.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
19
api/src/main/java/io/xpipe/api/XPipeServerException.java
Normal file
19
api/src/main/java/io/xpipe/api/XPipeServerException.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
115
core/src/main/java/io/xpipe/core/data/node/TupleNode.java
Normal file
115
core/src/main/java/io/xpipe/core/data/node/TupleNode.java
Normal 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();
|
||||
}
|
|
@ -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();
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package io.xpipe.core.data.type.callback;
|
||||
package io.xpipe.core.data.typed;
|
||||
|
||||
public class ReusableTypedDataStructureNodeCallback implements TypedDataStreamCallback {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.xpipe.core.data.type.callback;
|
||||
package io.xpipe.core.data.typed;
|
||||
|
||||
public interface TypedDataStreamCallback {
|
||||
|
|
@ -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();
|
|
@ -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++) {
|
|
@ -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) {
|
||||
if (!initialized) {
|
||||
readNode = ValueNode.wrap(data);
|
||||
return;
|
||||
}
|
||||
|
||||
children.peek().add(ValueNode.wrap(data));
|
||||
if (!flattened.get(dataTypeIndex).isArray()) {
|
||||
dataTypeIndex++;
|
||||
if (!flattened.get(currentDataTypeIndex).isArray()) {
|
||||
currentDataTypeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
|
@ -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();
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,5 +8,8 @@ public enum DataSourceType {
|
|||
TABLE,
|
||||
|
||||
@JsonProperty("structure")
|
||||
STRUCTURE
|
||||
STRUCTURE,
|
||||
|
||||
@JsonProperty("raw")
|
||||
RAW
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
public interface DataStructureConnection extends DataSourceConnection {
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
public interface StructureDataReadConnection extends DataSourceConnection {
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
62
core/src/test/java/io/xpipe/core/test/DataSourceIdTest.java
Normal file
62
core/src/test/java/io/xpipe/core/test/DataSourceIdTest.java
Normal 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 "));
|
||||
}
|
||||
}
|
56
core/src/test/java/io/xpipe/core/test/DataStructureTest.java
Normal file
56
core/src/test/java/io/xpipe/core/test/DataStructureTest.java
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
7
core/src/test/java/module-info.java
Normal file
7
core/src/test/java/module-info.java
Normal 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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue