Add support for more data source types

This commit is contained in:
Christopher Schnick 2022-05-03 15:26:31 +02:00
parent 7dec1afdb4
commit 100438744e
34 changed files with 669 additions and 92 deletions

View file

@ -11,6 +11,10 @@ apply from: "$rootDir/deps/lombok.gradle"
apply from: 'publish.gradle' apply from: 'publish.gradle'
apply from: "$rootDir/deps/publish-base.gradle" apply from: "$rootDir/deps/publish-base.gradle"
configurations {
compileOnly.extendsFrom(dep)
}
version = file('../version').text version = file('../version').text
group = 'io.xpipe' group = 'io.xpipe'
archivesBaseName = 'beacon' archivesBaseName = 'beacon'

View file

@ -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 {
}
}

View file

@ -33,12 +33,14 @@ module io.xpipe.beacon {
WriteExecuteExchange, WriteExecuteExchange,
SelectExchange, SelectExchange,
ReadPreparationExchange, ReadPreparationExchange,
QueryTextDataExchange,
ReadExecuteExchange, ReadExecuteExchange,
DialogExchange, DialogExchange,
QueryDataSourceExchange, QueryDataSourceExchange,
StoreStreamExchange, StoreStreamExchange,
EditPreparationExchange, EditPreparationExchange,
EditExecuteExchange, EditExecuteExchange,
ConvertExchange,
QueryTableDataExchange, QueryTableDataExchange,
VersionExchange; VersionExchange;
} }

View file

@ -12,6 +12,10 @@ apply from: "$rootDir/deps/junit.gradle"
apply from: 'publish.gradle' apply from: 'publish.gradle'
apply from: "$rootDir/deps/publish-base.gradle" apply from: "$rootDir/deps/publish-base.gradle"
configurations {
compileOnly.extendsFrom(dep)
}
version = file('../version').text version = file('../version').text
group = 'io.xpipe' group = 'io.xpipe'
archivesBaseName = 'core' archivesBaseName = 'core'

View file

@ -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);
}

View file

@ -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);
}
});
}
}
}

View file

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

View file

@ -27,11 +27,6 @@ public interface DataSourceDescriptor<DS extends DataStore> {
*/ */
DataSourceInfo determineInfo(DS store) throws Exception; DataSourceInfo determineInfo(DS store) throws Exception;
/**
* Returns the general data source type.
*/
DataSourceType getType();
DataSourceReadConnection openReadConnection(DS store) throws Exception; DataSourceReadConnection openReadConnection(DS store) throws Exception;
DataSourceConnection openWriteConnection(DS store) throws Exception; DataSourceConnection openWriteConnection(DS store) throws Exception;

View file

@ -7,8 +7,6 @@ import io.xpipe.core.data.type.TupleType;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Value; import lombok.Value;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.OptionalInt; import java.util.OptionalInt;
/** /**
@ -53,8 +51,11 @@ public abstract class DataSourceInfo {
@JsonTypeName("structure") @JsonTypeName("structure")
public static class Structure extends DataSourceInfo { public static class Structure extends DataSourceInfo {
int entries;
@JsonCreator @JsonCreator
public Structure() { public Structure(int entries) {
this.entries = entries;
} }
@Override @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) @EqualsAndHashCode(callSuper = false)
@Value @Value
@JsonTypeName("text") @JsonTypeName("text")
public static class Text extends DataSourceInfo { public static class Text extends DataSourceInfo {
Charset charset;
int lineCount; int lineCount;
@JsonCreator @JsonCreator
public Text(Charset charset, int lineCount) { public Text(int lineCount) {
this.charset = charset;
this.lineCount = lineCount; this.lineCount = lineCount;
} }
@ -89,12 +105,10 @@ public abstract class DataSourceInfo {
@JsonTypeName("raw") @JsonTypeName("raw")
public static class Raw extends DataSourceInfo { public static class Raw extends DataSourceInfo {
int byteCount; int byteCount;
ByteOrder byteOrder;
@JsonCreator @JsonCreator
public Raw(int byteCount, ByteOrder byteOrder) { public Raw(int byteCount) {
this.byteCount = byteCount; this.byteCount = byteCount;
this.byteOrder = byteOrder;
} }
@Override @Override

View file

@ -18,5 +18,8 @@ public enum DataSourceType {
TEXT, TEXT,
@JsonProperty("raw") @JsonProperty("raw")
RAW RAW,
@JsonProperty("collection")
COLLECTION
} }

View file

@ -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);
}

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,6 @@
package io.xpipe.core.source;
public interface RawWriteConnection extends DataSourceConnection {
void write(byte[] bytes) throws Exception;
}

View file

@ -1,27 +1,43 @@
package io.xpipe.core.source; package io.xpipe.core.source;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.store.DataStore; 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); var con = newReadConnection(store);
con.init(); con.init();
return con; return con;
} }
public final StructureWriteConnection openWriteConnection(DS store) throws Exception { default StructureWriteConnection openWriteConnection(DS store) throws Exception {
var con = newWriteConnection(store); var con = newWriteConnection(store);
con.init(); con.init();
return con; return con;
} }
StructureWriteConnection newWriteConnection(DS store);
protected abstract StructureWriteConnection newWriteConnection(DS store); StructureReadConnection newReadConnection(DS store);
protected abstract StructureReadConnection newReadConnection(DS store);
@Override
public DataSourceType getType() {
return DataSourceType.STRUCTURE;
}
} }

View file

@ -19,9 +19,4 @@ public abstract class TableDataSourceDescriptor<DS extends DataStore> implements
protected abstract TableWriteConnection newWriteConnection(DS store); protected abstract TableWriteConnection newWriteConnection(DS store);
protected abstract TableReadConnection newReadConnection(DS store); protected abstract TableReadConnection newReadConnection(DS store);
@Override
public DataSourceType getType() {
return DataSourceType.TABLE;
}
} }

View file

@ -4,9 +4,15 @@ import io.xpipe.core.store.DataStore;
public abstract class TextDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> { public abstract class TextDataSourceDescriptor<DS extends DataStore> implements DataSourceDescriptor<DS> {
private static final int MAX_LINE_READ = 1000;
@Override @Override
public DataSourceType getType() { public DataSourceInfo determineInfo(DS store) throws Exception {
return DataSourceType.TEXT; 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 @Override

View file

@ -1,23 +1,31 @@
package io.xpipe.core.source; package io.xpipe.core.source;
import java.util.List; import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream; import java.util.stream.Stream;
public interface TextReadConnection extends DataSourceReadConnection { 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; Stream<String> lines() throws Exception;
boolean isFinished() 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 { default void forward(DataSourceConnection con) throws Exception {
try (var tCon = (TextWriteConnection) con) { try (var tCon = (TextWriteConnection) con) {
tCon.init(); tCon.init();

View file

@ -1,6 +1,7 @@
package io.xpipe.core.util; package io.xpipe.core.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
@ -22,6 +23,7 @@ public class JacksonHelper {
ObjectMapper objectMapper = INSTANCE; ObjectMapper objectMapper = INSTANCE;
objectMapper.enable(SerializationFeature.INDENT_OUTPUT); objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.registerModules(findModules(layer)); objectMapper.registerModules(findModules(layer));
objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker() objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()

View file

@ -17,6 +17,10 @@ apply from: "$rootDir/deps/slf4j.gradle"
apply from: 'publish.gradle' apply from: 'publish.gradle'
apply from: "$rootDir/deps/publish-base.gradle" apply from: "$rootDir/deps/publish-base.gradle"
configurations {
compileOnly.extendsFrom(dep)
}
version = file('../version').text version = file('../version').text
group = 'io.xpipe' group = 'io.xpipe'
archivesBaseName = 'extension' archivesBaseName = 'extension'

View file

@ -12,31 +12,74 @@ import javafx.scene.layout.Region;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
public interface DataSourceProvider { 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 { interface FileProvider {
String getFileName(); String getFileName();
Map<Supplier<String>, String> getFileExtensions(); Map<String, List<String>> getFileExtensions();
}
interface GuiProvider {
Region createConfigOptions(DataStore input, Property<? extends DataSourceDescriptor<?>> source);
String getDisplayName();
String getDisplayImage();
Supplier<String> getDescription(DataSourceDescriptor<?> source);
} }
interface ConfigProvider { 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 ConfigOption
CHARSET_OPTION = new ConfigOption("Charset", "charset"); CHARSET_OPTION = new ConfigOption("Charset", "charset");
Function<String, Charset> Function<String, Charset>
@ -87,19 +130,45 @@ public interface DataSourceProvider {
List<String> getPossibleNames(); 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 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(); FileProvider getFileProvider();
GuiProvider getGuiProvider();
ConfigProvider getConfigProvider(); 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(); DataSourceDescriptor<?> createDefaultDescriptor();
@ -112,4 +181,6 @@ public interface DataSourceProvider {
DataSourceDescriptor<?> createDefaultWriteDescriptor(DataStore input, DataSourceInfo info) throws Exception; DataSourceDescriptor<?> createDefaultWriteDescriptor(DataStore input, DataSourceInfo info) throws Exception;
Class<? extends DataSourceDescriptor<?>> getDescriptorClass(); Class<? extends DataSourceDescriptor<?>> getDescriptorClass();
Optional<String> determineDefaultName(DataStore store);
} }

View file

@ -1,12 +1,13 @@
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.*;
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;
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.Optional;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.Set; import java.util.Set;
@ -23,35 +24,54 @@ public class DataSourceProviders {
} }
} }
public static DataSourceProvider getNativeProviderForType(DataSourceType t) { @SuppressWarnings("unchecked")
switch (t) { public static DataSourceDescriptor<StreamDataStore> getNativeDataSourceDescriptorForType(DataSourceType t) {
case TABLE -> { try {
return DataSourceProviders.byId("xpbt").orElseThrow(); return switch (t) {
} case TABLE -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("xpbt").orElseThrow()
case STRUCTURE -> { .getDescriptorClass().getConstructors()[0].newInstance();
return DataSourceProviders.byId("xpbs").orElseThrow(); case STRUCTURE -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("xpbs").orElseThrow()
} .getDescriptorClass().getConstructors()[0].newInstance();
case TEXT -> { case TEXT -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("text").orElseThrow()
return DataSourceProviders.byId("xpbx").orElseThrow(); .getDescriptorClass().getConstructors()[0].newInstance(StandardCharsets.UTF_8);
} case RAW -> (DataSourceDescriptor<StreamDataStore>) DataSourceProviders.byId("xpbr").orElseThrow()
case RAW -> { .getDescriptorClass().getConstructors()[0].newInstance();
return DataSourceProviders.byId("xpbb").orElseThrow(); };
} } catch (Exception ex) {
throw new AssertionError(ex);
} }
throw new AssertionError();
} }
@SuppressWarnings("unchecked") @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) { public static TableDataSourceDescriptor<LocalFileDataStore> createLocalTableDescriptor(TupleType type) {
try { return (TableDataSourceDescriptor<LocalFileDataStore>)
return (TableDataSourceDescriptor<LocalFileDataStore>) DataSourceProviders.byId("xpbt").orElseThrow().getDescriptorClass()
DataSourceProviders.byId("xpbt").orElseThrow().getDescriptorClass() .getDeclaredConstructors()[0].newInstance(type);
.getDeclaredConstructors()[0].newInstance(type);
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).terminal(true).build().handle();
return null;
}
} }
public static Optional<DataSourceProvider> byDescriptorClass(Class<?> clazz) { public static Optional<DataSourceProvider> byDescriptorClass(Class<?> clazz) {
@ -85,7 +105,7 @@ public class DataSourceProviders {
} }
return ALL.stream().filter(d -> d.getFileProvider() != null) 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() { public static Set<DataSourceProvider> getAll() {

View file

@ -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;
}
};
}
}

View file

@ -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();
}
}

View file

@ -7,10 +7,10 @@ module io.xpipe.extension {
exports io.xpipe.extension.event; exports io.xpipe.extension.event;
exports io.xpipe.extension.prefs; exports io.xpipe.extension.prefs;
requires io.xpipe.core; requires transitive io.xpipe.core;
requires javafx.base; requires transitive javafx.base;
requires javafx.graphics; requires javafx.graphics;
requires javafx.controls; requires transitive javafx.controls;
requires io.xpipe.fxcomps; requires io.xpipe.fxcomps;
requires org.apache.commons.collections4; requires org.apache.commons.collections4;
requires static lombok; requires static lombok;

View file

@ -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)
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -0,0 +1,3 @@
displayName=Mein Dateiformat
description=Meine Dateiformat-Beschreibung
fileName=Mein Dateiformat Datei

View file

@ -0,0 +1,3 @@
displayName=My file format
description=My file format description
fileName=My file format file