mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 15:10:23 +00:00
Add support for more data source types
This commit is contained in:
parent
7dec1afdb4
commit
100438744e
34 changed files with 669 additions and 92 deletions
|
@ -11,6 +11,10 @@ apply from: "$rootDir/deps/lombok.gradle"
|
|||
apply from: 'publish.gradle'
|
||||
apply from: "$rootDir/deps/publish-base.gradle"
|
||||
|
||||
configurations {
|
||||
compileOnly.extendsFrom(dep)
|
||||
}
|
||||
|
||||
version = file('../version').text
|
||||
group = 'io.xpipe'
|
||||
archivesBaseName = 'beacon'
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package io.xpipe.beacon.exchange.cli;
|
||||
|
||||
import io.xpipe.beacon.exchange.MessageExchange;
|
||||
import io.xpipe.beacon.message.RequestMessage;
|
||||
import io.xpipe.beacon.message.ResponseMessage;
|
||||
import io.xpipe.core.source.DataSourceConfigInstance;
|
||||
import io.xpipe.core.source.DataSourceId;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
import lombok.Builder;
|
||||
import lombok.NonNull;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
public class ConvertExchange implements MessageExchange<ConvertExchange.Request, ConvertExchange.Response> {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "convert";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ConvertExchange.Request> getRequestClass() {
|
||||
return ConvertExchange.Request.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ConvertExchange.Response> getResponseClass() {
|
||||
return ConvertExchange.Response.class;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {
|
||||
@NonNull
|
||||
DataSourceReference ref;
|
||||
|
||||
@NonNull DataSourceId copyId;
|
||||
|
||||
@NonNull
|
||||
DataSourceConfigInstance config;
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
}
|
||||
}
|
|
@ -33,12 +33,14 @@ module io.xpipe.beacon {
|
|||
WriteExecuteExchange,
|
||||
SelectExchange,
|
||||
ReadPreparationExchange,
|
||||
QueryTextDataExchange,
|
||||
ReadExecuteExchange,
|
||||
DialogExchange,
|
||||
QueryDataSourceExchange,
|
||||
StoreStreamExchange,
|
||||
EditPreparationExchange,
|
||||
EditExecuteExchange,
|
||||
ConvertExchange,
|
||||
QueryTableDataExchange,
|
||||
VersionExchange;
|
||||
}
|
|
@ -12,6 +12,10 @@ apply from: "$rootDir/deps/junit.gradle"
|
|||
apply from: 'publish.gradle'
|
||||
apply from: "$rootDir/deps/publish-base.gradle"
|
||||
|
||||
configurations {
|
||||
compileOnly.extendsFrom(dep)
|
||||
}
|
||||
|
||||
version = file('../version').text
|
||||
group = 'io.xpipe'
|
||||
archivesBaseName = 'core'
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
public interface CollectionDataSourceDescriptor<DS extends DataStore> extends DataSourceDescriptor<DS> {
|
||||
|
||||
@Override
|
||||
default DataSourceInfo determineInfo(DS store) throws Exception {
|
||||
try (var con = openReadConnection(store)) {
|
||||
var c = (int) con.listEntries().count();
|
||||
return new DataSourceInfo.Structure(c);
|
||||
}
|
||||
}
|
||||
|
||||
default CollectionReadConnection openReadConnection(DS store) throws Exception {
|
||||
var con = newReadConnection(store);
|
||||
con.init();
|
||||
return con;
|
||||
}
|
||||
|
||||
default CollectionWriteConnection openWriteConnection(DS store) throws Exception {
|
||||
var con = newWriteConnection(store);
|
||||
con.init();
|
||||
return con;
|
||||
}
|
||||
|
||||
CollectionWriteConnection newWriteConnection(DS store);
|
||||
|
||||
CollectionReadConnection newReadConnection(DS store);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface CollectionReadConnection extends DataSourceReadConnection {
|
||||
|
||||
<T extends DataSourceReadConnection> T open(String entry) throws Exception;
|
||||
|
||||
Stream<String> listEntries() throws Exception;
|
||||
|
||||
@SneakyThrows
|
||||
default void forward(DataSourceConnection con) throws Exception {
|
||||
try (var tCon = (CollectionWriteConnection) con) {
|
||||
tCon.init();
|
||||
listEntries().forEach(s -> {
|
||||
try (var subCon = open(s)) {
|
||||
((CollectionWriteConnection) con).write(s, subCon);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
public interface CollectionWriteConnection extends DataSourceConnection {
|
||||
|
||||
void write(String entry, DataSourceReadConnection con) throws Exception;
|
||||
}
|
|
@ -27,11 +27,6 @@ public interface DataSourceDescriptor<DS extends DataStore> {
|
|||
*/
|
||||
DataSourceInfo determineInfo(DS store) throws Exception;
|
||||
|
||||
/**
|
||||
* Returns the general data source type.
|
||||
*/
|
||||
DataSourceType getType();
|
||||
|
||||
DataSourceReadConnection openReadConnection(DS store) throws Exception;
|
||||
|
||||
DataSourceConnection openWriteConnection(DS store) throws Exception;
|
||||
|
|
|
@ -7,8 +7,6 @@ import io.xpipe.core.data.type.TupleType;
|
|||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
/**
|
||||
|
@ -53,8 +51,11 @@ public abstract class DataSourceInfo {
|
|||
@JsonTypeName("structure")
|
||||
public static class Structure extends DataSourceInfo {
|
||||
|
||||
int entries;
|
||||
|
||||
@JsonCreator
|
||||
public Structure() {
|
||||
public Structure(int entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,17 +64,32 @@ public abstract class DataSourceInfo {
|
|||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Value
|
||||
@JsonTypeName("collection")
|
||||
public static class Collection extends DataSourceInfo {
|
||||
|
||||
int entries;
|
||||
|
||||
@JsonCreator
|
||||
public Collection(int entries) {
|
||||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceType getType() {
|
||||
return DataSourceType.COLLECTION;
|
||||
}
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Value
|
||||
@JsonTypeName("text")
|
||||
public static class Text extends DataSourceInfo {
|
||||
Charset charset;
|
||||
|
||||
int lineCount;
|
||||
|
||||
@JsonCreator
|
||||
public Text(Charset charset, int lineCount) {
|
||||
this.charset = charset;
|
||||
public Text(int lineCount) {
|
||||
this.lineCount = lineCount;
|
||||
}
|
||||
|
||||
|
@ -89,12 +105,10 @@ public abstract class DataSourceInfo {
|
|||
@JsonTypeName("raw")
|
||||
public static class Raw extends DataSourceInfo {
|
||||
int byteCount;
|
||||
ByteOrder byteOrder;
|
||||
|
||||
@JsonCreator
|
||||
public Raw(int byteCount, ByteOrder byteOrder) {
|
||||
public Raw(int byteCount) {
|
||||
this.byteCount = byteCount;
|
||||
this.byteOrder = byteOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,5 +18,8 @@ public enum DataSourceType {
|
|||
TEXT,
|
||||
|
||||
@JsonProperty("raw")
|
||||
RAW
|
||||
RAW,
|
||||
|
||||
@JsonProperty("collection")
|
||||
COLLECTION
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
public abstract class RawDataSourceDescriptor <DS extends DataStore> implements DataSourceDescriptor<DS> {
|
||||
|
||||
private static final int MAX_BYTES_READ = 100000;
|
||||
|
||||
@Override
|
||||
public DataSourceInfo determineInfo(DS store) throws Exception {
|
||||
try (var con = openReadConnection(store)) {
|
||||
var b = con.readBytes(MAX_BYTES_READ);
|
||||
int usedCount = b.length == MAX_BYTES_READ ? -1 : b.length;
|
||||
return new DataSourceInfo.Raw(usedCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RawReadConnection openReadConnection(DS store) throws Exception {
|
||||
var con = newReadConnection(store);
|
||||
con.init();
|
||||
return con;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RawWriteConnection openWriteConnection(DS store) throws Exception {
|
||||
var con = newWriteConnection(store);
|
||||
con.init();
|
||||
return con;
|
||||
}
|
||||
|
||||
protected abstract RawWriteConnection newWriteConnection(DS store);
|
||||
|
||||
protected abstract RawReadConnection newReadConnection(DS store);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
public interface RawReadConnection extends DataSourceReadConnection {
|
||||
|
||||
byte[] readBytes(int max) throws Exception;
|
||||
|
||||
int BUFFER_SIZE = 8192;
|
||||
|
||||
default void forward(DataSourceConnection con) throws Exception {
|
||||
try (var tCon = (RawWriteConnection) con) {
|
||||
tCon.init();
|
||||
byte[] b;
|
||||
while ((b = readBytes(BUFFER_SIZE)).length > 0) {
|
||||
tCon.write(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
public interface RawWriteConnection extends DataSourceConnection {
|
||||
|
||||
void write(byte[] bytes) throws Exception;
|
||||
}
|
|
@ -1,27 +1,43 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
import io.xpipe.core.data.node.DataStructureNode;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
public abstract class StructureDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> {
|
||||
public interface StructureDataSourceDescriptor<DS extends DataStore> extends DataSourceDescriptor<DS> {
|
||||
|
||||
public final StructureReadConnection openReadConnection(DS store) throws Exception {
|
||||
private int countEntries(DataStructureNode n) {
|
||||
if (n.isValue()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int c = 0;
|
||||
for (int i = 0; i < n.size(); i++) {
|
||||
c += countEntries(n.at(i));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
default DataSourceInfo determineInfo(DS store) throws Exception {
|
||||
try (var con = openReadConnection(store)) {
|
||||
var n = con.read();
|
||||
var c = countEntries(n);
|
||||
return new DataSourceInfo.Structure(c);
|
||||
}
|
||||
}
|
||||
|
||||
default StructureReadConnection openReadConnection(DS store) throws Exception {
|
||||
var con = newReadConnection(store);
|
||||
con.init();
|
||||
return con;
|
||||
}
|
||||
|
||||
public final StructureWriteConnection openWriteConnection(DS store) throws Exception {
|
||||
default StructureWriteConnection openWriteConnection(DS store) throws Exception {
|
||||
var con = newWriteConnection(store);
|
||||
con.init();
|
||||
return con;
|
||||
}
|
||||
StructureWriteConnection newWriteConnection(DS store);
|
||||
|
||||
protected abstract StructureWriteConnection newWriteConnection(DS store);
|
||||
|
||||
protected abstract StructureReadConnection newReadConnection(DS store);
|
||||
|
||||
@Override
|
||||
public DataSourceType getType() {
|
||||
return DataSourceType.STRUCTURE;
|
||||
}
|
||||
StructureReadConnection newReadConnection(DS store);
|
||||
}
|
||||
|
|
|
@ -19,9 +19,4 @@ public abstract class TableDataSourceDescriptor<DS extends DataStore> implements
|
|||
protected abstract TableWriteConnection newWriteConnection(DS store);
|
||||
|
||||
protected abstract TableReadConnection newReadConnection(DS store);
|
||||
|
||||
@Override
|
||||
public DataSourceType getType() {
|
||||
return DataSourceType.TABLE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,15 @@ import io.xpipe.core.store.DataStore;
|
|||
|
||||
public abstract class TextDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> {
|
||||
|
||||
private static final int MAX_LINE_READ = 1000;
|
||||
|
||||
@Override
|
||||
public DataSourceType getType() {
|
||||
return DataSourceType.TEXT;
|
||||
public DataSourceInfo determineInfo(DS store) throws Exception {
|
||||
try (var con = openReadConnection(store)) {
|
||||
int count = (int) con.lines().limit(MAX_LINE_READ).count();
|
||||
int usedCount = count == MAX_LINE_READ ? -1 : count;
|
||||
return new DataSourceInfo.Text(usedCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,23 +1,31 @@
|
|||
package io.xpipe.core.source;
|
||||
|
||||
import java.util.List;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface TextReadConnection extends DataSourceReadConnection {
|
||||
|
||||
/**
|
||||
* 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 forwardLines(OutputStream out, int maxLines) throws Exception {
|
||||
if (maxLines == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
for (var it = lines().iterator(); it.hasNext(); counter++) {
|
||||
if (counter == maxLines) {
|
||||
break;
|
||||
}
|
||||
|
||||
out.write(it.next().getBytes(StandardCharsets.UTF_8));
|
||||
out.write("\n".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
default void forward(DataSourceConnection con) throws Exception {
|
||||
try (var tCon = (TextWriteConnection) con) {
|
||||
tCon.init();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.xpipe.core.util;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
@ -22,6 +23,7 @@ public class JacksonHelper {
|
|||
ObjectMapper objectMapper = INSTANCE;
|
||||
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
|
||||
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
|
||||
objectMapper.registerModules(findModules(layer));
|
||||
objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
|
||||
|
|
|
@ -17,6 +17,10 @@ apply from: "$rootDir/deps/slf4j.gradle"
|
|||
apply from: 'publish.gradle'
|
||||
apply from: "$rootDir/deps/publish-base.gradle"
|
||||
|
||||
configurations {
|
||||
compileOnly.extendsFrom(dep)
|
||||
}
|
||||
|
||||
version = file('../version').text
|
||||
group = 'io.xpipe'
|
||||
archivesBaseName = 'extension'
|
||||
|
|
|
@ -12,31 +12,74 @@ import javafx.scene.layout.Region;
|
|||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface DataSourceProvider {
|
||||
|
||||
default String i18n(String key) {
|
||||
return I18n.get(getId() + "." + key);
|
||||
}
|
||||
|
||||
default String i18nKey(String key) {
|
||||
return getId() + "." + key;
|
||||
}
|
||||
|
||||
default Region createConfigOptions(DataStore input, Property<? extends DataSourceDescriptor<?>> source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
default String getDisplayName() {
|
||||
return i18n("displayName");
|
||||
}
|
||||
|
||||
default String getDisplayImageFile() {
|
||||
return "logo.png";
|
||||
}
|
||||
|
||||
default String getDescription(DataSourceDescriptor<?> source) {
|
||||
return i18n("description");
|
||||
}
|
||||
|
||||
interface FileProvider {
|
||||
|
||||
String getFileName();
|
||||
|
||||
Map<Supplier<String>, String> getFileExtensions();
|
||||
}
|
||||
|
||||
interface GuiProvider {
|
||||
|
||||
Region createConfigOptions(DataStore input, Property<? extends DataSourceDescriptor<?>> source);
|
||||
|
||||
String getDisplayName();
|
||||
|
||||
String getDisplayImage();
|
||||
|
||||
Supplier<String> getDescription(DataSourceDescriptor<?> source);
|
||||
Map<String, List<String>> getFileExtensions();
|
||||
}
|
||||
|
||||
interface ConfigProvider {
|
||||
|
||||
static ConfigProvider empty(List<String> names, Supplier<DataSourceDescriptor<?>> supplier) {
|
||||
return new ConfigProvider() {
|
||||
@Override
|
||||
public ConfigOptionSet getConfig() {
|
||||
return ConfigOptionSet.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceDescriptor<?> toDescriptor(Map<String, String> values) {
|
||||
return supplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> toConfigOptions(DataSourceDescriptor<?> desc) {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ConfigOption, Function<String, ?>> getConverters() {
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPossibleNames() {
|
||||
return names;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ConfigOption
|
||||
CHARSET_OPTION = new ConfigOption("Charset", "charset");
|
||||
Function<String, Charset>
|
||||
|
@ -87,19 +130,45 @@ public interface DataSourceProvider {
|
|||
List<String> getPossibleNames();
|
||||
}
|
||||
|
||||
DataSourceType getType();
|
||||
default boolean isHidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
DataSourceType getPrimaryType();
|
||||
|
||||
/**
|
||||
* Checks whether this provider prefers a certain kind of store.
|
||||
* This is important for the correct autodetection of a store.
|
||||
*/
|
||||
boolean prefersStore(DataStore store);
|
||||
|
||||
boolean supportsStore(DataStore store);
|
||||
/**
|
||||
* Checks whether this provider supports the store in principle.
|
||||
* This method should not perform any further checks,
|
||||
* just check whether it may be possible that the store is supported.
|
||||
*
|
||||
* This method will be called for validation purposes.
|
||||
*/
|
||||
boolean couldSupportStore(DataStore store);
|
||||
|
||||
/**
|
||||
* Performs a deep inspection to check whether this provider supports a given store.
|
||||
*
|
||||
* This functionality will be used in case no preferred provider has been found.
|
||||
*/
|
||||
default boolean supportsStore(DataStore store) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileProvider getFileProvider();
|
||||
|
||||
GuiProvider getGuiProvider();
|
||||
|
||||
ConfigProvider getConfigProvider();
|
||||
|
||||
String getId();
|
||||
default String getId() {
|
||||
var n = getClass().getPackageName();
|
||||
var i = n.lastIndexOf('.');
|
||||
return i != -1 ? n.substring(i + 1) : n;
|
||||
}
|
||||
|
||||
DataSourceDescriptor<?> createDefaultDescriptor();
|
||||
|
||||
|
@ -112,4 +181,6 @@ public interface DataSourceProvider {
|
|||
DataSourceDescriptor<?> createDefaultWriteDescriptor(DataStore input, DataSourceInfo info) throws Exception;
|
||||
|
||||
Class<? extends DataSourceDescriptor<?>> getDescriptorClass();
|
||||
|
||||
Optional<String> determineDefaultName(DataStore store);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.data.type.TupleType;
|
||||
import io.xpipe.core.source.DataSourceType;
|
||||
import io.xpipe.core.source.TableDataSourceDescriptor;
|
||||
import io.xpipe.core.source.*;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.LocalFileDataStore;
|
||||
import io.xpipe.extension.event.ErrorEvent;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
@ -23,35 +24,54 @@ 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();
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static DataSourceDescriptor<StreamDataStore> getNativeDataSourceDescriptorForType(DataSourceType t) {
|
||||
try {
|
||||
return switch (t) {
|
||||
case TABLE -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("xpbt").orElseThrow()
|
||||
.getDescriptorClass().getConstructors()[0].newInstance();
|
||||
case STRUCTURE -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("xpbs").orElseThrow()
|
||||
.getDescriptorClass().getConstructors()[0].newInstance();
|
||||
case TEXT -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("text").orElseThrow()
|
||||
.getDescriptorClass().getConstructors()[0].newInstance(StandardCharsets.UTF_8);
|
||||
case RAW -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("xpbr").orElseThrow()
|
||||
.getDescriptorClass().getConstructors()[0].newInstance();
|
||||
};
|
||||
} catch (Exception ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static StructureDataSourceDescriptor<LocalFileDataStore> createLocalStructureDescriptor() {
|
||||
return (StructureDataSourceDescriptor<LocalFileDataStore>)
|
||||
DataSourceProviders.byId("json").orElseThrow().getDescriptorClass()
|
||||
.getDeclaredConstructors()[0].newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static RawDataSourceDescriptor<LocalFileDataStore> createLocalRawDescriptor() {
|
||||
return (RawDataSourceDescriptor<LocalFileDataStore>)
|
||||
DataSourceProviders.byId("binary").orElseThrow().getDescriptorClass()
|
||||
.getDeclaredConstructors()[0].newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static TextDataSourceDescriptor<LocalFileDataStore> createLocalTextDescriptor() {
|
||||
return (TextDataSourceDescriptor<LocalFileDataStore>)
|
||||
DataSourceProviders.byId("text").orElseThrow().getDescriptorClass()
|
||||
.getDeclaredConstructors()[0].newInstance();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@SneakyThrows
|
||||
public static TableDataSourceDescriptor<LocalFileDataStore> createLocalTableDescriptor(TupleType type) {
|
||||
try {
|
||||
return (TableDataSourceDescriptor<LocalFileDataStore>)
|
||||
DataSourceProviders.byId("xpbt").orElseThrow().getDescriptorClass()
|
||||
.getDeclaredConstructors()[0].newInstance(type);
|
||||
} catch (Exception ex) {
|
||||
ErrorEvent.fromThrowable(ex).terminal(true).build().handle();
|
||||
return null;
|
||||
}
|
||||
return (TableDataSourceDescriptor<LocalFileDataStore>)
|
||||
DataSourceProviders.byId("xpbt").orElseThrow().getDescriptorClass()
|
||||
.getDeclaredConstructors()[0].newInstance(type);
|
||||
}
|
||||
|
||||
public static Optional<DataSourceProvider> byDescriptorClass(Class<?> clazz) {
|
||||
|
@ -85,7 +105,7 @@ public class DataSourceProviders {
|
|||
}
|
||||
|
||||
return ALL.stream().filter(d -> d.getFileProvider() != null)
|
||||
.filter(d -> d.supportsStore(store)).findAny();
|
||||
.filter(d -> d.couldSupportStore(store)).findAny();
|
||||
}
|
||||
|
||||
public static Set<DataSourceProvider> getAll() {
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.FileDataStore;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface SimpleFileDataSourceProvider extends DataSourceProvider {
|
||||
|
||||
@Override
|
||||
default Optional<String> determineDefaultName(DataStore store) {
|
||||
if (store instanceof FileDataStore l) {
|
||||
var n = l.getFileName();
|
||||
var i = n.lastIndexOf('.');
|
||||
return Optional.of(i != -1 ? n.substring(0, i) : n);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean prefersStore(DataStore store) {
|
||||
for (var e : getSupportedExtensions().entrySet()) {
|
||||
if (e.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (store instanceof FileDataStore l) {
|
||||
return l.getFileName().matches("\\." + e.getValue() + "$");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean couldSupportStore(DataStore store) {
|
||||
return store instanceof StreamDataStore;
|
||||
}
|
||||
|
||||
default String getNameI18nKey() {
|
||||
return i18nKey("displayName");
|
||||
}
|
||||
Map<String, List<String>> getSupportedExtensions();
|
||||
|
||||
@Override
|
||||
default FileProvider getFileProvider() {
|
||||
return new FileProvider() {
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return I18n.get(getNameI18nKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getFileExtensions() {
|
||||
var map = new LinkedHashMap<>(getSupportedExtensions());
|
||||
return map;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.xpipe.extension;
|
||||
|
||||
import io.xpipe.core.source.DataSourceDescriptor;
|
||||
import io.xpipe.core.source.DataSourceInfo;
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
|
||||
public interface UniformDataSourceProvider extends DataSourceProvider {
|
||||
|
||||
@Override
|
||||
default ConfigProvider getConfigProvider() {
|
||||
return ConfigProvider.empty(List.of(getId()), this::createDefaultDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
default DataSourceDescriptor<?> createDefaultDescriptor() {
|
||||
try {
|
||||
return (DataSourceDescriptor<?>) getDescriptorClass().getDeclaredConstructors()[0].newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default DataSourceDescriptor<?> createDefaultDescriptor(DataStore input) throws Exception {
|
||||
return createDefaultDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
default DataSourceDescriptor<?> createDefaultWriteDescriptor(DataStore input, DataSourceInfo info) throws Exception {
|
||||
return createDefaultDescriptor();
|
||||
}
|
||||
}
|
|
@ -7,10 +7,10 @@ module io.xpipe.extension {
|
|||
exports io.xpipe.extension.event;
|
||||
exports io.xpipe.extension.prefs;
|
||||
|
||||
requires io.xpipe.core;
|
||||
requires javafx.base;
|
||||
requires transitive io.xpipe.core;
|
||||
requires transitive javafx.base;
|
||||
requires javafx.graphics;
|
||||
requires javafx.controls;
|
||||
requires transitive javafx.controls;
|
||||
requires io.xpipe.fxcomps;
|
||||
requires org.apache.commons.collections4;
|
||||
requires static lombok;
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id "org.moditect.gradleplugin" version "1.0.0-rc3"
|
||||
}
|
||||
|
||||
apply from: "$rootDir/deps/java.gradle"
|
||||
apply from: "$rootDir/deps/javafx.gradle"
|
||||
apply from: "$rootDir/deps/lombok.gradle"
|
||||
apply from: "$rootDir/deps/extension.gradle"
|
||||
|
||||
configurations {
|
||||
compileOnly.extendsFrom(dep)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package io.xpipe.ext.json;
|
||||
|
||||
import io.xpipe.core.source.RawDataSourceDescriptor;
|
||||
import io.xpipe.core.source.RawReadConnection;
|
||||
import io.xpipe.core.source.RawWriteConnection;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
|
||||
public class MyRawFileDescriptor extends RawDataSourceDescriptor<StreamDataStore> {
|
||||
@Override
|
||||
protected RawWriteConnection newWriteConnection(StreamDataStore store) {
|
||||
return new MyRawFileWriteConnection(store);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RawReadConnection newReadConnection(StreamDataStore store) {
|
||||
return new MyRawFileReadConnection(store);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package io.xpipe.ext.json;
|
||||
|
||||
import io.xpipe.core.source.DataSourceDescriptor;
|
||||
import io.xpipe.core.source.DataSourceType;
|
||||
import io.xpipe.extension.DataSourceProvider;
|
||||
import io.xpipe.extension.SimpleFileDataSourceProvider;
|
||||
import io.xpipe.extension.UniformDataSourceProvider;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MyRawFileProvider implements UniformDataSourceProvider, SimpleFileDataSourceProvider, DataSourceProvider {
|
||||
|
||||
@Override
|
||||
public DataSourceType getPrimaryType() {
|
||||
return DataSourceType.RAW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getSupportedExtensions() {
|
||||
var map = new LinkedHashMap<String, String>();
|
||||
map.put(i18nKey("fileName"), "myf");
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends DataSourceDescriptor<?>> getDescriptorClass() {
|
||||
return MyRawFileDescriptor.class;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package io.xpipe.ext.json;
|
||||
|
||||
import io.xpipe.core.source.RawReadConnection;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public class MyRawFileReadConnection implements RawReadConnection {
|
||||
|
||||
private InputStream inputStream;
|
||||
private final StreamDataStore store;
|
||||
|
||||
public MyRawFileReadConnection(StreamDataStore store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
if (inputStream != null) {
|
||||
throw new IllegalStateException("Already initialized");
|
||||
}
|
||||
|
||||
inputStream = store.openInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (inputStream == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readBytes(int max) throws Exception {
|
||||
if (inputStream == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
return inputStream.readNBytes(max);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package io.xpipe.ext.json;
|
||||
|
||||
import io.xpipe.core.source.RawWriteConnection;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class MyRawFileWriteConnection implements RawWriteConnection {
|
||||
|
||||
private final StreamDataStore store;
|
||||
private OutputStream outputStream;
|
||||
|
||||
public MyRawFileWriteConnection(StreamDataStore store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
if (outputStream != null) {
|
||||
throw new IllegalStateException("Already initialized");
|
||||
}
|
||||
|
||||
outputStream = store.openOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
if (outputStream == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] bytes) throws Exception {
|
||||
if (outputStream == null) {
|
||||
throw new IllegalStateException("Not initialized");
|
||||
}
|
||||
|
||||
outputStream.write(bytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import io.xpipe.ext.json.MyRawFileProvider;
|
||||
import io.xpipe.extension.DataSourceProvider;
|
||||
|
||||
module io.xpipe.ext.file_data_source_sample {
|
||||
exports io.xpipe.ext.json;
|
||||
|
||||
opens io.xpipe.ext.json;
|
||||
|
||||
requires io.xpipe.core;
|
||||
requires io.xpipe.extension;
|
||||
|
||||
provides DataSourceProvider with MyRawFileProvider;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
|
@ -0,0 +1,3 @@
|
|||
displayName=Mein Dateiformat
|
||||
description=Meine Dateiformat-Beschreibung
|
||||
fileName=Mein Dateiformat Datei
|
|
@ -0,0 +1,3 @@
|
|||
displayName=My file format
|
||||
description=My file format description
|
||||
fileName=My file format file
|
Loading…
Reference in a new issue