Add textual flag to value node, prepare for text data sources, and various other small fixes

This commit is contained in:
Christopher Schnick 2022-04-13 20:35:16 +02:00
parent c7c86b1bf8
commit 7dec1afdb4
43 changed files with 414 additions and 128 deletions

View file

@ -59,7 +59,7 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
List<DataStructureNode> nodes = new ArrayList<>(); List<DataStructureNode> nodes = new ArrayList<>();
XPipeConnection.execute(con -> { XPipeConnection.execute(con -> {
var req = QueryTableDataExchange.Request.builder() var req = QueryTableDataExchange.Request.builder()
.id(getId()).maxRows(maxRows).build(); .ref(DataSourceReference.id(getId())).maxRows(maxRows).build();
con.performInputExchange(req, (QueryTableDataExchange.Response res, InputStream in) -> { con.performInputExchange(req, (QueryTableDataExchange.Response res, InputStream in) -> {
var r = new TypedDataStreamParser(info.getDataType()); var r = new TypedDataStreamParser(info.getDataType());
r.parseStructures(in, TypedDataStructureNodeReader.immutable(info.getDataType()), nodes::add); r.parseStructures(in, TypedDataStructureNodeReader.immutable(info.getDataType()), nodes::add);
@ -82,7 +82,7 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
connection = XPipeConnection.open(); connection = XPipeConnection.open();
var req = QueryTableDataExchange.Request.builder() var req = QueryTableDataExchange.Request.builder()
.id(getId()).build(); .ref(DataSourceReference.id(getId())).build();
connection.sendRequest(req); connection.sendRequest(req);
connection.receiveResponse(); connection.receiveResponse();
connection.receiveBody(); connection.receiveBody();

View file

@ -138,6 +138,11 @@ public class BeaconClient implements AutoCloseable {
System.out.println("Sending request to server of type " + req.getClass().getName()); System.out.println("Sending request to server of type " + req.getClass().getName());
} }
if (BeaconConfig.debugEnabled()) {
System.out.println("Sending raw request:");
System.out.println(msg.toPrettyString());
}
try { try {
var mapper = JacksonHelper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); var mapper = JacksonHelper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
var gen = mapper.createGenerator(socket.getOutputStream()); var gen = mapper.createGenerator(socket.getOutputStream());

View file

@ -43,7 +43,8 @@ public class ReadPreparationExchange implements MessageExchange<ReadPreparationE
@Builder @Builder
@Value @Value
public static class Response implements ResponseMessage { public static class Response implements ResponseMessage {
String determinedType;
DataSourceConfigInstance config; DataSourceConfigInstance config;
StreamDataStore store;
} }
} }

View file

@ -3,7 +3,7 @@ package io.xpipe.beacon.exchange.api;
import io.xpipe.beacon.exchange.MessageExchange; import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage; import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage; import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceId; import io.xpipe.core.source.DataSourceReference;
import lombok.Builder; import lombok.Builder;
import lombok.NonNull; import lombok.NonNull;
import lombok.Value; import lombok.Value;
@ -34,7 +34,7 @@ public class QueryTableDataExchange implements MessageExchange<QueryTableDataExc
@Value @Value
public static class Request implements RequestMessage { public static class Request implements RequestMessage {
@NonNull @NonNull
DataSourceId id; DataSourceReference ref;
int maxRows; int maxRows;
} }

View file

@ -0,0 +1,44 @@
package io.xpipe.beacon.exchange.api;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.beacon.message.RequestMessage;
import io.xpipe.beacon.message.ResponseMessage;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
public class QueryTextDataExchange implements MessageExchange<QueryTextDataExchange.Request, QueryTextDataExchange.Response> {
@Override
public String getId() {
return "queryTextData";
}
@Override
public Class<QueryTextDataExchange.Request> getRequestClass() {
return QueryTextDataExchange.Request.class;
}
@Override
public Class<QueryTextDataExchange.Response> getResponseClass() {
return QueryTextDataExchange.Response.class;
}
@Jacksonized
@Builder
@Value
public static class Request implements RequestMessage {
@NonNull
DataSourceReference ref;
int maxLines;
}
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {
}
}

View file

@ -0,0 +1,14 @@
package io.xpipe.core.config;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value;
@Value
@Builder
@AllArgsConstructor(onConstructor_={@JsonCreator})
public class ConfigOption {
String name;
String key;
}

View file

@ -0,0 +1,23 @@
package io.xpipe.core.config;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
import java.util.List;
@Value
@Builder
@AllArgsConstructor(onConstructor_={@JsonCreator})
public class ConfigOptionSet {
public static ConfigOptionSet empty() {
return new ConfigOptionSet(List.of());
}
@Singular
List<ConfigOption> options;
}

View file

@ -0,0 +1,26 @@
package io.xpipe.core.config;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.util.Map;
@Value
@AllArgsConstructor(onConstructor_={@JsonCreator})
public class ConfigOptionSetInstance {
/**
* The available configuration options.
*/
ConfigOptionSet configOptions;
/**
* The current configuration options that are set.
*/
Map<String, String> currentValues;
public boolean isComplete() {
return currentValues.size() == configOptions.getOptions().size();
}
}

View file

@ -127,9 +127,9 @@ public class GenericArrayReader implements GenericAbstractReader {
} }
@Override @Override
public void onValue(byte[] value) { public void onValue(byte[] value, boolean textual) {
if (currentReader != null) { if (currentReader != null) {
currentReader.onValue(value); currentReader.onValue(value, textual);
return; return;
} }
@ -141,7 +141,7 @@ public class GenericArrayReader implements GenericAbstractReader {
throw new IllegalStateException("Array is full but got another value"); throw new IllegalStateException("Array is full but got another value");
} }
put(ValueNode.mutable(value)); put(ValueNode.mutable(value, textual));
} }
@Override @Override

View file

@ -17,6 +17,6 @@ public interface GenericDataStreamCallback {
default void onTupleEnd() { default void onTupleEnd() {
} }
default void onValue(byte[] value) { default void onValue(byte[] value, boolean textual) {
} }
} }

View file

@ -79,8 +79,9 @@ public class GenericDataStreamParser {
} }
private static void parseValue(InputStream in, GenericDataStreamCallback cb) throws IOException { private static void parseValue(InputStream in, GenericDataStreamCallback cb) throws IOException {
var textual = in.read() != 0;
var size = in.read(); var size = in.read();
var data = in.readNBytes(size); var data = in.readNBytes(size);
cb.onValue(data); cb.onValue(data, textual);
} }
} }

View file

@ -53,6 +53,7 @@ public class GenericDataStreamWriter {
private static void writeValue(OutputStream out, ValueNode value) throws IOException { private static void writeValue(OutputStream out, ValueNode value) throws IOException {
out.write(DataStructureNodeIO.GENERIC_VALUE_ID); out.write(DataStructureNodeIO.GENERIC_VALUE_ID);
out.write(value.isTextual() ? 1 : 0);
out.write(value.getRawData().length); out.write(value.getRawData().length);
out.write(value.getRawData()); out.write(value.getRawData());
} }

View file

@ -78,12 +78,12 @@ public class GenericDataStructureNodeReader implements GenericDataStreamCallback
} }
@Override @Override
public void onValue(byte[] value) { public void onValue(byte[] value, boolean textual) {
if (hasReader()) { if (hasReader()) {
reader.onValue(value); reader.onValue(value, textual);
return; return;
} }
node = ValueNode.mutable(value); node = ValueNode.mutable(value, textual);
} }
} }

View file

@ -139,9 +139,9 @@ public class GenericTupleReader implements GenericAbstractReader {
} }
@Override @Override
public void onValue(byte[] value) { public void onValue(byte[] value, boolean textual) {
if (currentReader != null) { if (currentReader != null) {
currentReader.onValue(value); currentReader.onValue(value, textual);
return; return;
} }
@ -153,7 +153,7 @@ public class GenericTupleReader implements GenericAbstractReader {
throw new IllegalStateException("Tuple is full but got another value"); throw new IllegalStateException("Tuple is full but got another value");
} }
putNode(ValueNode.mutable(value)); putNode(ValueNode.mutable(value, textual));
} }
@Override @Override

View file

@ -51,12 +51,20 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
throw unsupported("clear"); throw unsupported("clear");
} }
public DataStructureNode setRawData(byte[] data) { public boolean isTextual() {
throw unsupported("textual check");
}
public DataStructureNode setRaw(byte[] data) {
throw unsupported("set raw data"); throw unsupported("set raw data");
} }
public DataStructureNode setNull() { public DataStructureNode set(Object newValue) {
throw unsupported("set null"); throw unsupported("set");
}
public DataStructureNode set(Object newValue, boolean textual) {
throw unsupported("set");
} }
public DataStructureNode set(int index, DataStructureNode node) { public DataStructureNode set(int index, DataStructureNode node) {

View file

@ -3,14 +3,16 @@ package io.xpipe.core.data.node;
public class ImmutableValueNode extends ValueNode { public class ImmutableValueNode extends ValueNode {
private final byte[] data; private final byte[] data;
private final boolean textual;
ImmutableValueNode(byte[] data) { ImmutableValueNode(byte[] data, boolean textual) {
this.data = data; this.data = data;
this.textual = textual;
} }
@Override @Override
public String toString(int indent) { public String toString(int indent) {
return new String(data) + "(I)"; return (textual ? "\"" : "") + new String(data) + (textual ? "\"" : "") + " (I)";
} }
@Override @Override
@ -25,11 +27,26 @@ public class ImmutableValueNode extends ValueNode {
@Override @Override
public ValueNode mutableCopy() { public ValueNode mutableCopy() {
return ValueNode.mutable(data); return ValueNode.mutable(data, textual);
} }
@Override @Override
public DataStructureNode setRawData(byte[] data) { public boolean isTextual() {
return textual;
}
@Override
public DataStructureNode setRaw(byte[] data) {
throw new UnsupportedOperationException("Value node is immutable");
}
@Override
public DataStructureNode set(Object newValue) {
throw new UnsupportedOperationException("Value node is immutable");
}
@Override
public DataStructureNode set(Object newValue, boolean textual) {
throw new UnsupportedOperationException("Value node is immutable"); throw new UnsupportedOperationException("Value node is immutable");
} }

View file

@ -1,16 +1,27 @@
package io.xpipe.core.data.node; package io.xpipe.core.data.node;
import java.nio.charset.StandardCharsets;
public class MutableValueNode extends ValueNode { public class MutableValueNode extends ValueNode {
private byte[] data; static final MutableValueNode NULL = new MutableValueNode(null, false);
MutableValueNode(byte[] data) { private byte[] data;
private boolean textual;
MutableValueNode(byte[] data, boolean textual) {
this.data = data; this.data = data;
this.textual = textual;
} }
@Override @Override
public String toString(int indent) { public String toString(int indent) {
return new String(data) + "(M)"; return (textual ? "\"" : "") + new String(data) + (textual ? "\"" : "") + " (M)";
}
@Override
public boolean isTextual() {
return textual;
} }
@Override @Override
@ -20,20 +31,49 @@ public class MutableValueNode extends ValueNode {
@Override @Override
public ValueNode immutableView() { public ValueNode immutableView() {
return new ImmutableValueNode(data); return new ImmutableValueNode(data, textual);
} }
@Override @Override
public ValueNode mutableCopy() { public ValueNode mutableCopy() {
return new MutableValueNode(data); return new MutableValueNode(data, textual);
} }
@Override @Override
public DataStructureNode setRawData(byte[] data) { public DataStructureNode setRaw(byte[] data) {
this.data = data; this.data = data;
return this; return this;
} }
@Override
public DataStructureNode set(Object newValue) {
if (newValue == null) {
this.data = null;
this.textual = false;
} else {
setRaw(newValue.toString().getBytes(StandardCharsets.UTF_8));
}
return this;
}
@Override
public DataStructureNode set(Object newValue, boolean textual) {
if (newValue == null && textual) {
throw new IllegalArgumentException("Can't set a textual null");
}
if (newValue == null) {
this.data = null;
this.textual = false;
} else {
setRaw(newValue.toString().getBytes(StandardCharsets.UTF_8));
this.textual = textual;
}
return this;
}
public byte[] getRawData() { public byte[] getRawData() {
return data; return data;
} }

View file

@ -8,8 +8,6 @@ import java.util.Arrays;
public abstract class ValueNode extends DataStructureNode { public abstract class ValueNode extends DataStructureNode {
private static final byte[] NULL = new byte[]{0};
protected ValueNode() { protected ValueNode() {
} }
@ -31,40 +29,57 @@ public abstract class ValueNode extends DataStructureNode {
@Override @Override
public abstract ValueNode mutableCopy(); public abstract ValueNode mutableCopy();
public static ValueNode immutable(byte[] data) { public static ValueNode immutable(byte[] data, boolean textual) {
return new ImmutableValueNode(data); return new ImmutableValueNode(data, textual);
} }
public static ValueNode immutable(Object o) { public static ValueNode immutable(Object o, boolean textual) {
return immutable(o.toString().getBytes(StandardCharsets.UTF_8)); return immutable(o.toString().getBytes(StandardCharsets.UTF_8), textual);
}
public static ValueNode immutableNull() {
return MutableValueNode.NULL.immutableView();
} }
public static ValueNode mutableNull() { public static ValueNode mutableNull() {
return mutable(NULL); return MutableValueNode.NULL.mutableCopy();
} }
public static ValueNode nullValue() { public static ValueNode mutable(byte[] data, boolean textual) {
return mutable(NULL); return new MutableValueNode(data, textual);
} }
public static ValueNode mutable(byte[] data) { public static ValueNode mutable(Object o, boolean textual) {
return new MutableValueNode(data); return mutable(o.toString().getBytes(StandardCharsets.UTF_8), textual);
}
public static ValueNode mutable(Object o) {
return mutable(o.toString().getBytes(StandardCharsets.UTF_8));
} }
public static ValueNode of(byte[] data) { public static ValueNode of(byte[] data) {
return mutable(data); return mutable(data, false);
} }
public static ValueNode of(Object o) { public static ValueNode of(Object o) {
return mutable(o); return mutable(o, false);
}
public static ValueNode ofText(byte[] data) {
return mutable(data, true);
}
public static ValueNode ofText(Object o) {
return mutable(o, true);
} }
@Override @Override
public abstract DataStructureNode setRawData(byte[] data); public abstract boolean isTextual();
@Override
public abstract DataStructureNode setRaw(byte[] data);
@Override
public abstract DataStructureNode set(Object newValue);
@Override
public abstract DataStructureNode set(Object newValue, boolean textual);
@Override @Override
public final int asInt() { public final int asInt() {

View file

@ -5,7 +5,7 @@ import io.xpipe.core.data.type.TupleType;
public interface TypedDataStreamCallback { public interface TypedDataStreamCallback {
default void onValue(byte[] data) { default void onValue(byte[] data, boolean textual) {
} }
default void onGenericNode(DataStructureNode node) { default void onGenericNode(DataStructureNode node) {

View file

@ -129,8 +129,9 @@ public class TypedDataStreamParser {
} }
private void parseValue(InputStream in, TypedDataStreamCallback cb) throws IOException { private void parseValue(InputStream in, TypedDataStreamCallback cb) throws IOException {
var textual = in.read() != 0;
var size = in.read(); var size = in.read();
var data = in.readNBytes(size); var data = in.readNBytes(size);
cb.onValue(data); cb.onValue(data, textual);
} }
} }

View file

@ -32,6 +32,7 @@ public class TypedDataStreamWriter {
private static void writeValue(OutputStream out, ValueNode n) throws IOException { private static void writeValue(OutputStream out, ValueNode n) throws IOException {
out.write(DataStructureNodeIO.TYPED_VALUE_ID); out.write(DataStructureNodeIO.TYPED_VALUE_ID);
out.write(n.isTextual() ? 1 : 0);
out.write(n.getRawData().length); out.write(n.getRawData().length);
out.write(n.getRawData()); out.write(n.getRawData());
} }

View file

@ -81,12 +81,12 @@ public class TypedDataStructureNodeReader implements TypedAbstractReader {
} }
@Override @Override
public void onValue(byte[] data) { public void onValue(byte[] data, boolean textual) {
if (!expectedType.isValue()) { if (!expectedType.isValue()) {
throw new IllegalStateException("Expected " + expectedType.getName() + " but got value"); throw new IllegalStateException("Expected " + expectedType.getName() + " but got value");
} }
var val = makeImmutable ? ValueNode.immutable(data) : ValueNode.mutable(data); var val = makeImmutable ? ValueNode.immutable(data, textual) : ValueNode.mutable(data, textual);
finishNode(val); finishNode(val);
moveExpectedType(false); moveExpectedType(false);
} }

View file

@ -47,16 +47,16 @@ public class TypedReusableDataStructureNodeReader implements TypedAbstractReader
} }
@Override @Override
public void onValue(byte[] data) { public void onValue(byte[] data, boolean textual) {
if (!initialized()) { if (!initialized()) {
initialReader.onValue(data); initialReader.onValue(data, textual);
return; return;
} }
if (isInArray()) { if (isInArray()) {
getCurrentParent().set(indices.peek(), ValueNode.mutable(data)); getCurrentParent().set(indices.peek(), ValueNode.mutable(data, textual));
} else { } else {
getCurrent().setRawData(data); getCurrent().setRaw(data);
} }
if (!indices.isEmpty()) { if (!indices.isEmpty()) {

View file

@ -1,9 +1,10 @@
package io.xpipe.core.source; package io.xpipe.core.source;
import com.fasterxml.jackson.annotation.JsonCreator;
import io.xpipe.core.config.ConfigOptionSet;
import io.xpipe.core.config.ConfigOptionSetInstance;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Value; import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import java.util.Map; import java.util.Map;
@ -12,13 +13,11 @@ import java.util.Map;
* This configuration can either be in progress or complete. * This configuration can either be in progress or complete.
*/ */
@Value @Value
@Builder @AllArgsConstructor(onConstructor_={@JsonCreator})
@Jacksonized
@AllArgsConstructor
public class DataSourceConfigInstance { public class DataSourceConfigInstance {
public static DataSourceConfigInstance xpbt() { public static DataSourceConfigInstance xpbt() {
return new DataSourceConfigInstance("xpbt", DataSourceConfigOptions.empty(), Map.of()); return new DataSourceConfigInstance("xpbt", ConfigOptionSet.empty(), Map.of());
} }
/** /**
@ -29,7 +28,7 @@ public class DataSourceConfigInstance {
/** /**
* The available configuration options. * The available configuration options.
*/ */
DataSourceConfigOptions configOptions; ConfigOptionSet configOptions;
/** /**
* The current configuration options that are set. * The current configuration options that are set.
@ -39,4 +38,10 @@ public class DataSourceConfigInstance {
public boolean isComplete() { public boolean isComplete() {
return currentValues.size() == configOptions.getOptions().size(); return currentValues.size() == configOptions.getOptions().size();
} }
public DataSourceConfigInstance(String provider, ConfigOptionSetInstance cInstance) {
this.provider = provider;
this.configOptions = cInstance.getConfigOptions();
this.currentValues = cInstance.getCurrentValues();
}
} }

View file

@ -1,32 +0,0 @@
package io.xpipe.core.source;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Singular;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import java.util.List;
@Value
@Builder
@Jacksonized
public class DataSourceConfigOptions {
public static DataSourceConfigOptions empty() {
return new DataSourceConfigOptions(List.of());
}
@Singular
List<Option> options;
@Value
@Builder
@Jacksonized
@AllArgsConstructor
public static class Option {
String name;
String key;
}
}

View file

@ -31,4 +31,8 @@ public interface DataSourceDescriptor<DS extends DataStore> {
* Returns the general data source type. * Returns the general data source type.
*/ */
DataSourceType getType(); DataSourceType getType();
DataSourceReadConnection openReadConnection(DS store) throws Exception;
DataSourceConnection openWriteConnection(DS store) throws Exception;
} }

View file

@ -67,11 +67,14 @@ public abstract class DataSourceInfo {
@Value @Value
@JsonTypeName("text") @JsonTypeName("text")
public static class Text extends DataSourceInfo { public static class Text extends DataSourceInfo {
Charset encoding; Charset charset;
int lineCount;
@JsonCreator @JsonCreator
public Text(Charset encoding) { public Text(Charset charset, int lineCount) {
this.encoding = encoding; this.charset = charset;
this.lineCount = lineCount;
} }
@Override @Override

View file

@ -0,0 +1,6 @@
package io.xpipe.core.source;
public interface DataSourceReadConnection extends DataSourceConnection {
void forward(DataSourceConnection con) throws Exception;
}

View file

@ -2,10 +2,17 @@ package io.xpipe.core.source;
import io.xpipe.core.data.node.DataStructureNode; import io.xpipe.core.data.node.DataStructureNode;
public interface StructureReadConnection extends DataSourceConnection { public interface StructureReadConnection extends DataSourceReadConnection {
/** /**
* Reads the complete contents. * Reads the complete contents.
*/ */
DataStructureNode read() throws Exception; DataStructureNode read() throws Exception;
default void forward(DataSourceConnection con) throws Exception {
try (var tCon = (StructureWriteConnection) con) {
tCon.init();
tCon.write(read());
}
}
} }

View file

@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicInteger;
/** /**
* A connection for sequentially reading the data of a table data source. * A connection for sequentially reading the data of a table data source.
*/ */
public interface TableReadConnection extends DataSourceConnection { public interface TableReadConnection extends DataSourceReadConnection {
/** /**
* Returns the data type of the table data. * Returns the data type of the table data.
@ -39,6 +39,10 @@ public interface TableReadConnection extends DataSourceConnection {
* Writes the rows to an OutputStream in the X-Pipe binary format. * Writes the rows to an OutputStream in the X-Pipe binary format.
*/ */
default void forwardRows(OutputStream out, int maxLines) throws Exception { default void forwardRows(OutputStream out, int maxLines) throws Exception {
if (maxLines == 0) {
return;
}
var dataType = getDataType(); var dataType = getDataType();
AtomicInteger rowCounter = new AtomicInteger(); AtomicInteger rowCounter = new AtomicInteger();
withRows(l -> { withRows(l -> {
@ -47,4 +51,11 @@ public interface TableReadConnection extends DataSourceConnection {
return rowCounter.get() != maxLines; return rowCounter.get() != maxLines;
}); });
} }
default void forward(DataSourceConnection con) throws Exception {
try (var tCon = (TableWriteConnection) con) {
tCon.init();
withRows(tCon.writeLinesAcceptor());
}
}
} }

View file

@ -2,15 +2,28 @@ package io.xpipe.core.source;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
public class TextDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> { public abstract class TextDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> {
@Override
public DataSourceInfo determineInfo(DS store) throws Exception {
return null;
}
@Override @Override
public DataSourceType getType() { public DataSourceType getType() {
return DataSourceType.TEXT; return DataSourceType.TEXT;
} }
@Override
public TextReadConnection openReadConnection(DS store) throws Exception {
var con = newReadConnection(store);
con.init();
return con;
}
@Override
public TextWriteConnection openWriteConnection(DS store) throws Exception {
var con = newWriteConnection(store);
con.init();
return con;
}
protected abstract TextWriteConnection newWriteConnection(DS store);
protected abstract TextReadConnection newReadConnection(DS store);
} }

View file

@ -1,8 +1,29 @@
package io.xpipe.core.source; package io.xpipe.core.source;
import java.nio.charset.Charset; import java.util.List;
import java.util.stream.Stream;
public interface TextReadConnection extends DataSourceConnection { public interface TextReadConnection extends DataSourceReadConnection {
Charset getEncoding(); /**
* Reads the complete contents.
*/
String readAll() throws Exception;
List<String> readAllLines() throws Exception;
String readLine() throws Exception;
Stream<String> lines() throws Exception;
boolean isFinished() throws Exception;
default void forward(DataSourceConnection con) throws Exception {
try (var tCon = (TextWriteConnection) con) {
tCon.init();
for (var it = lines().iterator(); it.hasNext(); ) {
tCon.writeLine(it.next());
}
}
}
} }

View file

@ -1,8 +1,6 @@
package io.xpipe.core.source; package io.xpipe.core.source;
import java.io.OutputStream;
public interface TextWriteConnection extends DataSourceConnection { public interface TextWriteConnection extends DataSourceConnection {
OutputStream getOutputStream(); void writeLine(String line) throws Exception;
} }

View file

@ -0,0 +1,6 @@
package io.xpipe.core.store;
public interface FileDataStore extends StreamDataStore {
String getFileName();
}

View file

@ -15,7 +15,7 @@ import java.util.Optional;
@JsonTypeName("local") @JsonTypeName("local")
@EqualsAndHashCode @EqualsAndHashCode
public class LocalFileDataStore implements StreamDataStore { public class LocalFileDataStore implements FileDataStore {
private final Path file; private final Path file;
@ -57,4 +57,9 @@ public class LocalFileDataStore implements StreamDataStore {
public boolean exists() { public boolean exists() {
return Files.exists(file); return Files.exists(file);
} }
@Override
public String getFileName() {
return file.getFileName().toString();
}
} }

View file

@ -33,7 +33,10 @@ public class CoreJacksonModule extends SimpleModule {
new NamedType(TupleType.class), new NamedType(TupleType.class),
new NamedType(ArrayType.class), new NamedType(ArrayType.class),
new NamedType(WildcardType.class), new NamedType(WildcardType.class),
new NamedType(DataSourceInfo.Table.class) new NamedType(DataSourceInfo.Table.class),
new NamedType(DataSourceInfo.Structure.class),
new NamedType(DataSourceInfo.Text.class),
new NamedType(DataSourceInfo.Raw.class)
); );
addSerializer(Charset.class, new CharsetSerializer()); addSerializer(Charset.class, new CharsetSerializer());

View file

@ -16,6 +16,8 @@ module io.xpipe.core {
opens io.xpipe.core.util; opens io.xpipe.core.util;
opens io.xpipe.core.data.node; opens io.xpipe.core.data.node;
opens io.xpipe.core.data.typed; opens io.xpipe.core.data.typed;
exports io.xpipe.core.config;
opens io.xpipe.core.config;
requires com.fasterxml.jackson.core; requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind; requires com.fasterxml.jackson.databind;

View file

@ -134,7 +134,7 @@ public class DataStructureTest {
readNode.clear(); readNode.clear();
Assertions.assertEquals(readNode.size(), 0); Assertions.assertEquals(readNode.size(), 0);
} else { } else {
readNode.setRawData("abc".getBytes(StandardCharsets.UTF_8)); readNode.setRaw("abc".getBytes(StandardCharsets.UTF_8));
} }
}); });
if (readNode.isTuple() || readNode.isArray()) { if (readNode.isTuple() || readNode.isArray()) {

View file

@ -58,7 +58,7 @@ public class DataStructureTests {
} }
private static DataStructureNode createTestData12() { private static DataStructureNode createTestData12() {
var val = ValueNode.nullValue(); var val = ValueNode.of(null);
var flatArray = ArrayNode.of(); var flatArray = ArrayNode.of();
var flatTuple = TupleNode.builder().add("key1", val).build(); var flatTuple = TupleNode.builder().add("key1", val).build();
var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple)); var nestedArray = ArrayNode.of(List.of(flatArray, flatTuple));
@ -101,7 +101,7 @@ public class DataStructureTests {
public static DataStructureNode createTestData32() { public static DataStructureNode createTestData32() {
var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8)); var val = ValueNode.of("value2".getBytes(StandardCharsets.UTF_8));
var flatTuple = TupleNode.builder().add("key1", ValueNode.nullValue()).add("key2", ValueNode.nullValue()).build(); var flatTuple = TupleNode.builder().add("key1", ValueNode.of(null)).add("key2", ValueNode.of(null)).build();
var flatArray = ArrayNode.of(List.of(val, flatTuple)); var flatArray = ArrayNode.of(List.of(val, flatTuple));
return flatArray; return flatArray;
} }
@ -112,13 +112,13 @@ public class DataStructureTests {
} }
public static DataStructureNode createTestData42() { public static DataStructureNode createTestData42() {
var val = ValueNode.nullValue(); var val = ValueNode.of(null);
return val; return val;
} }
public static DataStructureNode createTestData51() { public static DataStructureNode createTestData51() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8)); var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var flatArray = ArrayNode.of(List.of(val, ValueNode.nullValue())); var flatArray = ArrayNode.of(List.of(val, ValueNode.of(null)));
var array1 = ArrayNode.of(List.of(flatArray)); var array1 = ArrayNode.of(List.of(flatArray));
var array2 = ArrayNode.of(List.of(array1, array1)); var array2 = ArrayNode.of(List.of(array1, array1));
return array2; return array2;
@ -143,7 +143,7 @@ public class DataStructureTests {
public static DataStructureNode createTestData61() { public static DataStructureNode createTestData61() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8)); var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var array = ArrayNode.of(List.of(val, ValueNode.nullValue())); var array = ArrayNode.of(List.of(val, ValueNode.of(null)));
var tuple = TupleNode.builder() var tuple = TupleNode.builder()
.add(val).add("key2", array).build(); .add(val).add("key2", array).build();
return tuple; return tuple;
@ -176,7 +176,7 @@ public class DataStructureTests {
public static DataStructureNode createTestData73() { public static DataStructureNode createTestData73() {
var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8)); var val = ValueNode.of("value".getBytes(StandardCharsets.UTF_8));
var array = ArrayNode.of(List.of(val, ValueNode.nullValue())); var array = ArrayNode.of(List.of(val, ValueNode.of(null)));
return array; return array;
} }

2
deps

@ -1 +1 @@
Subproject commit cbac51bc63e727c0ff5038cace4e91daa168a556 Subproject commit 49a1ad06bc6872f72c1d20ea864d24f3df59b7c5

View file

@ -1,11 +1,15 @@
package io.xpipe.extension; package io.xpipe.extension;
import io.xpipe.core.source.*; import io.xpipe.core.config.ConfigOption;
import io.xpipe.core.config.ConfigOptionSet;
import io.xpipe.core.source.DataSourceDescriptor;
import io.xpipe.core.source.DataSourceInfo;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.StreamDataStore;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
@ -15,8 +19,6 @@ public interface DataSourceProvider {
interface FileProvider { interface FileProvider {
void write(StreamDataStore store, DataSourceDescriptor<StreamDataStore> desc, TableReadConnection con) throws Exception;
String getFileName(); String getFileName();
Map<Supplier<String>, String> getFileExtensions(); Map<Supplier<String>, String> getFileExtensions();
@ -35,6 +37,13 @@ public interface DataSourceProvider {
interface ConfigProvider { interface ConfigProvider {
ConfigOption
CHARSET_OPTION = new ConfigOption("Charset", "charset");
Function<String, Charset>
CHARSET_CONVERTER = ConfigProvider.charsetConverter();
Function<Charset, String>
CHARSET_STRING = Charset::name;
static String booleanName(String name) { static String booleanName(String name) {
return name + " (y/n)"; return name + " (y/n)";
} }
@ -63,19 +72,25 @@ public interface DataSourceProvider {
}; };
} }
DataSourceConfigOptions getConfig(); static Function<String, Charset> charsetConverter() {
return Charset::forName;
}
ConfigOptionSet getConfig();
DataSourceDescriptor<?> toDescriptor(Map<String, String> values); DataSourceDescriptor<?> toDescriptor(Map<String, String> values);
Map<String, String> toConfigOptions(DataSourceDescriptor<?> desc); Map<String, String> toConfigOptions(DataSourceDescriptor<?> desc);
Map<DataSourceConfigOptions.Option, Function<String, ?>> getConverters(); Map<ConfigOption, Function<String, ?>> getConverters();
List<String> getPossibleNames(); List<String> getPossibleNames();
} }
DataSourceType getType(); DataSourceType getType();
boolean prefersStore(DataStore store);
boolean supportsStore(DataStore store); boolean supportsStore(DataStore store);
FileProvider getFileProvider(); FileProvider getFileProvider();

View file

@ -1,6 +1,7 @@
package io.xpipe.extension; package io.xpipe.extension;
import io.xpipe.core.data.type.TupleType; import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.source.TableDataSourceDescriptor; import io.xpipe.core.source.TableDataSourceDescriptor;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.LocalFileDataStore; import io.xpipe.core.store.LocalFileDataStore;
@ -22,6 +23,25 @@ public class DataSourceProviders {
} }
} }
public static DataSourceProvider getNativeProviderForType(DataSourceType t) {
switch (t) {
case TABLE -> {
return DataSourceProviders.byId("xpbt").orElseThrow();
}
case STRUCTURE -> {
return DataSourceProviders.byId("xpbs").orElseThrow();
}
case TEXT -> {
return DataSourceProviders.byId("xpbx").orElseThrow();
}
case RAW -> {
return DataSourceProviders.byId("xpbb").orElseThrow();
}
}
throw new AssertionError();
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static TableDataSourceDescriptor<LocalFileDataStore> createLocalTableDescriptor(TupleType type) { public static TableDataSourceDescriptor<LocalFileDataStore> createLocalTableDescriptor(TupleType type) {
try { try {

View file

@ -36,7 +36,9 @@ public class CharChoiceComp extends Comp<CompStructure<HBox>> {
public CompStructure<HBox> createBase() { public CompStructure<HBox> createBase() {
var charChoice = new CharComp(value); var charChoice = new CharComp(value);
var rangeCopy = new DualLinkedHashBidiMap<>(range); var rangeCopy = new DualLinkedHashBidiMap<>(range);
if (customName != null) {
rangeCopy.put(null, customName); rangeCopy.put(null, customName);
}
var choice = new ChoiceComp<Character>(charChoiceValue, rangeCopy); var choice = new ChoiceComp<Character>(charChoiceValue, rangeCopy);
var charChoiceR = charChoice.createRegion(); var charChoiceR = charChoice.createRegion();
var choiceR = choice.createRegion(); var choiceR = choice.createRegion();