mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-22 07:30:24 +00:00
Change structure of data sources, rework comps
This commit is contained in:
parent
948dcf33ef
commit
f4f9c1d978
40 changed files with 719 additions and 193 deletions
|
@ -60,7 +60,7 @@ public abstract class DataSourceImpl implements DataSource {
|
||||||
});
|
});
|
||||||
|
|
||||||
var configInstance = startRes.getConfig();
|
var configInstance = startRes.getConfig();
|
||||||
configInstance.getCurrentValues().putAll(config);
|
configInstance.getConfigInstance().getCurrentValues().putAll(config);
|
||||||
var endReq = ReadExecuteExchange.Request.builder()
|
var endReq = ReadExecuteExchange.Request.builder()
|
||||||
.target(id).dataStore(store).config(configInstance).build();
|
.target(id).dataStore(store).config(configInstance).build();
|
||||||
XPipeConnection.execute(con -> {
|
XPipeConnection.execute(con -> {
|
||||||
|
|
|
@ -37,6 +37,8 @@ public class ReadPreparationExchange implements MessageExchange<ReadPreparationE
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
StreamDataStore store;
|
StreamDataStore store;
|
||||||
|
|
||||||
|
boolean configureAll;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Jacksonized
|
@Jacksonized
|
||||||
|
@ -45,6 +47,7 @@ public class ReadPreparationExchange implements MessageExchange<ReadPreparationE
|
||||||
public static class Response implements ResponseMessage {
|
public static class Response implements ResponseMessage {
|
||||||
String determinedType;
|
String determinedType;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
DataSourceConfigInstance config;
|
DataSourceConfigInstance config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ public class DialogExchange implements MessageExchange<DialogExchange.Request, D
|
||||||
@Builder
|
@Builder
|
||||||
@Value
|
@Value
|
||||||
public static class Response implements ResponseMessage {
|
public static class Response implements ResponseMessage {
|
||||||
DataSourceConfigInstance instance;
|
|
||||||
String errorMsg;
|
String errorMsg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ apply from: "$rootDir/deps/java.gradle"
|
||||||
apply from: "$rootDir/deps/commons.gradle"
|
apply from: "$rootDir/deps/commons.gradle"
|
||||||
apply from: "$rootDir/deps/junit.gradle"
|
apply from: "$rootDir/deps/junit.gradle"
|
||||||
apply from: "$rootDir/deps/lombok.gradle"
|
apply from: "$rootDir/deps/lombok.gradle"
|
||||||
|
apply from: "$rootDir/deps/jackson.gradle"
|
||||||
|
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
compileOnly.extendsFrom(dep)
|
compileOnly.extendsFrom(dep)
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
package io.xpipe.charsetter;
|
package io.xpipe.charsetter;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
import org.apache.commons.io.ByteOrderMark;
|
import org.apache.commons.io.ByteOrderMark;
|
||||||
import org.apache.commons.io.input.BOMInputStream;
|
import org.apache.commons.io.input.BOMInputStream;
|
||||||
import org.apache.commons.lang3.function.FailableBiConsumer;
|
import org.apache.commons.lang3.function.FailableConsumer;
|
||||||
import org.apache.commons.lang3.function.FailableSupplier;
|
import org.apache.commons.lang3.function.FailableSupplier;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.*;
|
import java.nio.charset.*;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Charsetter {
|
public class Charsetter {
|
||||||
|
|
||||||
|
@ -25,7 +30,13 @@ public class Charsetter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Charset read(FailableSupplier<InputStream, Exception> in, FailableBiConsumer<InputStream, Charset, Exception> con) throws Exception {
|
@Value
|
||||||
|
public static class Result {
|
||||||
|
Charset charset;
|
||||||
|
NewLine newLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result read(FailableSupplier<InputStream, Exception> in, FailableConsumer<InputStreamReader, Exception> con) throws Exception {
|
||||||
checkInit();
|
checkInit();
|
||||||
|
|
||||||
try (var is = in.get();
|
try (var is = in.get();
|
||||||
|
@ -34,20 +45,53 @@ public class Charsetter {
|
||||||
String charsetName = bom == null ? null : bom.getCharsetName();
|
String charsetName = bom == null ? null : bom.getCharsetName();
|
||||||
var charset = charsetName != null ? Charset.forName(charsetName) : null;
|
var charset = charsetName != null ? Charset.forName(charsetName) : null;
|
||||||
|
|
||||||
|
bin.mark(MAX_BYTES);
|
||||||
|
var bytes = bin.readNBytes(MAX_BYTES);
|
||||||
|
bin.reset();
|
||||||
if (charset == null) {
|
if (charset == null) {
|
||||||
bin.mark(MAX_BYTES);
|
|
||||||
var bytes = bin.readNBytes(MAX_BYTES);
|
|
||||||
bin.reset();
|
|
||||||
charset = inferCharset(bytes);
|
charset = inferCharset(bytes);
|
||||||
}
|
}
|
||||||
|
var nl = inferNewLine(bytes);
|
||||||
|
|
||||||
if (con != null) {
|
if (con != null) {
|
||||||
con.accept(bin, charset);
|
con.accept(new InputStreamReader(bin, charset));
|
||||||
}
|
}
|
||||||
return charset;
|
return new Result(charset, nl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static NewLine inferNewLine(byte[] content) {
|
||||||
|
Map<NewLine, Integer> count = new HashMap<>();
|
||||||
|
for (var nl : NewLine.values()) {
|
||||||
|
var nlBytes = nl.getNewLine().getBytes(StandardCharsets.UTF_8);
|
||||||
|
count.put(nl, count(content, nlBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count.values().stream().allMatch(v -> v == 0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count.entrySet().stream().min(Comparator.comparingInt(Map.Entry::getValue))
|
||||||
|
.orElseThrow().getKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int count(byte[] outerArray, byte[] smallerArray) {
|
||||||
|
int count = 0;
|
||||||
|
for(int i = 0; i < outerArray.length - smallerArray.length+1; ++i) {
|
||||||
|
boolean found = true;
|
||||||
|
for(int j = 0; j < smallerArray.length; ++j) {
|
||||||
|
if (outerArray[i+j] != smallerArray[j]) {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
public static Charset inferCharset(byte[] content) {
|
public static Charset inferCharset(byte[] content) {
|
||||||
checkInit();
|
checkInit();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.charsetter;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@ -10,6 +11,10 @@ import java.util.Locale;
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class CharsetterContext {
|
public class CharsetterContext {
|
||||||
|
|
||||||
|
public static CharsetterContext empty() {
|
||||||
|
return new CharsetterContext(Charset.defaultCharset().name(), Locale.getDefault(), Locale.getDefault(), List.of());
|
||||||
|
}
|
||||||
|
|
||||||
String systemCharsetName;
|
String systemCharsetName;
|
||||||
|
|
||||||
Locale systemLocale;
|
Locale systemLocale;
|
||||||
|
|
41
charsetter/src/main/java/io/xpipe/charsetter/NewLine.java
Normal file
41
charsetter/src/main/java/io/xpipe/charsetter/NewLine.java
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package io.xpipe.charsetter;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public enum NewLine {
|
||||||
|
|
||||||
|
@JsonProperty("lf")
|
||||||
|
LF("\n", "lf"),
|
||||||
|
@JsonProperty("crlf")
|
||||||
|
CRLF("\r\n", "crlf");
|
||||||
|
|
||||||
|
public static NewLine platform() {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(n -> n.getNewLine().equals(System.getProperty("line.separator")))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NewLine id(String id) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(n -> n.getId().equalsIgnoreCase(id))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String newLine;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
NewLine(String newLine, String id) {
|
||||||
|
this.newLine = newLine;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNewLine() {
|
||||||
|
return newLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,4 +4,5 @@ module io.xpipe.charsetter {
|
||||||
requires org.apache.commons.io;
|
requires org.apache.commons.io;
|
||||||
requires org.apache.commons.lang3;
|
requires org.apache.commons.lang3;
|
||||||
requires static lombok;
|
requires static lombok;
|
||||||
|
requires com.fasterxml.jackson.databind;
|
||||||
}
|
}
|
86
core/src/main/java/io/xpipe/core/config/ConfigConverter.java
Normal file
86
core/src/main/java/io/xpipe/core/config/ConfigConverter.java
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package io.xpipe.core.config;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
public abstract class ConfigConverter<T> {
|
||||||
|
|
||||||
|
public static final ConfigConverter<Charset> CHARSET = new ConfigConverter<Charset>() {
|
||||||
|
@Override
|
||||||
|
protected Charset fromString(String s) {
|
||||||
|
return Charset.forName(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toString(Charset value) {
|
||||||
|
return value.name();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final ConfigConverter<String> STRING = new ConfigConverter<String>() {
|
||||||
|
@Override
|
||||||
|
protected String fromString(String s) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toString(String value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final ConfigConverter<Character> CHARACTER = new ConfigConverter<Character>() {
|
||||||
|
@Override
|
||||||
|
protected Character fromString(String s) {
|
||||||
|
if (s.length() != 1) {
|
||||||
|
throw new IllegalArgumentException("Invalid character: " + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.toCharArray()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toString(Character value) {
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final ConfigConverter<Boolean> BOOLEAN = new ConfigConverter<Boolean>() {
|
||||||
|
@Override
|
||||||
|
protected Boolean fromString(String s) {
|
||||||
|
if (s.equalsIgnoreCase("y") || s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("true")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.equalsIgnoreCase("n") || s.equalsIgnoreCase("no") || s.equalsIgnoreCase("false")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Invalid boolean: " + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String toString(Boolean value) {
|
||||||
|
return value ? "yes" : "no";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public T convertFromString(String s) {
|
||||||
|
if (s == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fromString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String convertToString(T value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract T fromString(String s);
|
||||||
|
|
||||||
|
protected abstract String toString(T value);
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
26
core/src/main/java/io/xpipe/core/config/ConfigParameter.java
Normal file
26
core/src/main/java/io/xpipe/core/config/ConfigParameter.java
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package io.xpipe.core.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ConfigParameter {
|
||||||
|
String key;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public ConfigParameter(String key) {
|
||||||
|
this.key = key;
|
||||||
|
this.converter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
ConfigConverter<?> converter;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> ConfigConverter<T> getConverter() {
|
||||||
|
return (ConfigConverter<T>) converter;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package io.xpipe.core.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
@AllArgsConstructor(onConstructor_={@JsonCreator})
|
||||||
|
public class ConfigParameterSetInstance {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The available configuration parameters.
|
||||||
|
*/
|
||||||
|
List<ConfigParameter> configParameters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current configuration options that are set.
|
||||||
|
*/
|
||||||
|
Map<String, String> currentValues;
|
||||||
|
|
||||||
|
public ConfigParameterSetInstance(Map<ConfigParameter, Object> map) {
|
||||||
|
configParameters = map.keySet().stream().toList();
|
||||||
|
currentValues = map.entrySet().stream().collect(Collectors.toMap(
|
||||||
|
e -> e.getKey().getKey(),
|
||||||
|
e -> e.getKey().getConverter().convertToString(e.getValue())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <X, T extends Function<X,?>> ConfigParameterSetInstance(Map<ConfigParameter, T> map, Object v) {
|
||||||
|
configParameters = map.keySet().stream().toList();
|
||||||
|
currentValues = map.entrySet().stream().collect(Collectors.toMap(
|
||||||
|
e -> e.getKey().getKey(),
|
||||||
|
e -> e.getKey().getConverter().convertToString(apply(e.getValue(), v))));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <X, T extends Function<X,?>, V> Object apply(T func, Object v) {
|
||||||
|
return func.apply((X) v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(ConfigParameter p, String val) {
|
||||||
|
currentValues.put(p.getKey(), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<ConfigParameter, Object> evaluate() {
|
||||||
|
return configParameters.stream().collect(Collectors.toMap(
|
||||||
|
p -> p,
|
||||||
|
p -> p.getConverter().convertFromString(currentValues.get(p.getKey()))));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package io.xpipe.core.connection;
|
||||||
|
|
||||||
|
public class Connection {
|
||||||
|
}
|
|
@ -8,6 +8,10 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
public abstract class ArrayNode extends DataStructureNode {
|
public abstract class ArrayNode extends DataStructureNode {
|
||||||
|
|
||||||
|
public static ArrayNode empty() {
|
||||||
|
return of(List.of());
|
||||||
|
}
|
||||||
|
|
||||||
public static ArrayNode of(DataStructureNode... dsn) {
|
public static ArrayNode of(DataStructureNode... dsn) {
|
||||||
return of(List.of(dsn));
|
return of(List.of(dsn));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ import com.fasterxml.jackson.databind.util.TokenBuffer;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
import io.xpipe.core.util.JacksonHelper;
|
import io.xpipe.core.util.JacksonHelper;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.NonNull;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -15,19 +16,20 @@ import java.util.Optional;
|
||||||
*
|
*
|
||||||
* This instance is only valid in combination with its associated data store instance.
|
* This instance is only valid in combination with its associated data store instance.
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public abstract class DataSource<DS extends DataStore> {
|
public abstract class DataSource<DS extends DataStore> {
|
||||||
|
|
||||||
@NonNull
|
|
||||||
protected DS store;
|
protected DS store;
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public DataSource<DS> copy() {
|
public <T extends DataSource<DS>> T copy() {
|
||||||
var mapper = JacksonHelper.newMapper();
|
var mapper = JacksonHelper.newMapper();
|
||||||
TokenBuffer tb = new TokenBuffer(mapper, false);
|
TokenBuffer tb = new TokenBuffer(mapper, false);
|
||||||
mapper.writeValue(tb, this);
|
mapper.writeValue(tb, this);
|
||||||
return mapper.readValue(tb.asParser(), getClass());
|
return (T) mapper.readValue(tb.asParser(), getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataSource<DS> withStore(DS store) {
|
public DataSource<DS> withStore(DS store) {
|
||||||
|
@ -36,6 +38,10 @@ public abstract class DataSource<DS extends DataStore> {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isComplete() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Casts this instance to the required type without checking whether a cast is possible.
|
* Casts this instance to the required type without checking whether a cast is possible.
|
||||||
*/
|
*/
|
||||||
|
@ -63,6 +69,10 @@ public abstract class DataSource<DS extends DataStore> {
|
||||||
|
|
||||||
public abstract DataSourceConnection openWriteConnection() throws Exception;
|
public abstract DataSourceConnection openWriteConnection() throws Exception;
|
||||||
|
|
||||||
|
public DataSourceConnection openAppendingWriteConnection() throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Appending write is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
public DS getStore() {
|
public DS getStore() {
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package io.xpipe.core.source;
|
package io.xpipe.core.source;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
import io.xpipe.core.config.ConfigOptionSet;
|
import io.xpipe.core.config.ConfigParameter;
|
||||||
import io.xpipe.core.config.ConfigOptionSetInstance;
|
import io.xpipe.core.config.ConfigParameterSetInstance;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the current configuration of a data source.
|
* Represents the current configuration of a data source.
|
||||||
|
@ -17,7 +18,7 @@ import java.util.Map;
|
||||||
public class DataSourceConfigInstance {
|
public class DataSourceConfigInstance {
|
||||||
|
|
||||||
public static DataSourceConfigInstance xpbt() {
|
public static DataSourceConfigInstance xpbt() {
|
||||||
return new DataSourceConfigInstance("xpbt", ConfigOptionSet.empty(), Map.of());
|
return new DataSourceConfigInstance("xpbt", new ConfigParameterSetInstance(Map.of()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,22 +27,21 @@ public class DataSourceConfigInstance {
|
||||||
String provider;
|
String provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The available configuration options.
|
* The available configuration parameters.
|
||||||
*/
|
*/
|
||||||
ConfigOptionSet configOptions;
|
ConfigParameterSetInstance configInstance;
|
||||||
|
|
||||||
/**
|
public DataSourceConfigInstance(String provider, Map<ConfigParameter, Object> map) {
|
||||||
* The current configuration options that are set.
|
this.provider = provider;
|
||||||
*/
|
this.configInstance = new ConfigParameterSetInstance(map);
|
||||||
Map<String, String> currentValues;
|
|
||||||
|
|
||||||
public boolean isComplete() {
|
|
||||||
return currentValues.size() == configOptions.getOptions().size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataSourceConfigInstance(String provider, ConfigOptionSetInstance cInstance) {
|
public <X, T extends Function<X,?>> DataSourceConfigInstance(String provider, Map<ConfigParameter, T> map, Object val) {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
this.configOptions = cInstance.getConfigOptions();
|
this.configInstance = new ConfigParameterSetInstance(map, val);
|
||||||
this.currentValues = cInstance.getCurrentValues();
|
}
|
||||||
|
|
||||||
|
public Map<ConfigParameter, Object> evaluate() {
|
||||||
|
return configInstance.evaluate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
package io.xpipe.core.source;
|
package io.xpipe.core.source;
|
||||||
|
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@NoArgsConstructor
|
||||||
public abstract class TableDataSource<DS extends DataStore> extends DataSource<DS> {
|
public abstract class TableDataSource<DS extends DataStore> extends DataSource<DS> {
|
||||||
|
|
||||||
public TableDataSource(DS store) {
|
public TableDataSource(DS store) {
|
||||||
|
@ -29,7 +35,15 @@ public abstract class TableDataSource<DS extends DataStore> extends DataSource<D
|
||||||
return con;
|
return con;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final TableWriteConnection openAppendingWriteConnection() throws Exception {
|
||||||
|
var con = newAppendingWriteConnection();
|
||||||
|
con.init();
|
||||||
|
return con;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract TableWriteConnection newWriteConnection();
|
protected abstract TableWriteConnection newWriteConnection();
|
||||||
|
|
||||||
|
protected abstract TableWriteConnection newAppendingWriteConnection();
|
||||||
|
|
||||||
protected abstract TableReadConnection newReadConnection();
|
protected abstract TableReadConnection newReadConnection();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,30 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
*/
|
*/
|
||||||
public interface TableReadConnection extends DataSourceReadConnection {
|
public interface TableReadConnection extends DataSourceReadConnection {
|
||||||
|
|
||||||
|
public static TableReadConnection empty() {
|
||||||
|
return new TableReadConnection() {
|
||||||
|
@Override
|
||||||
|
public TupleType getDataType() throws Exception {
|
||||||
|
return TupleType.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() throws Exception {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArrayNode readRows(int maxLines) throws Exception {
|
||||||
|
return ArrayNode.of();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the data type of the table data.
|
* Returns the data type of the table data.
|
||||||
*/
|
*/
|
||||||
|
@ -53,9 +77,7 @@ public interface TableReadConnection extends DataSourceReadConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
default void forward(DataSourceConnection con) throws Exception {
|
default void forward(DataSourceConnection con) throws Exception {
|
||||||
try (var tCon = (TableWriteConnection) con) {
|
var tCon = (TableWriteConnection) con;
|
||||||
tCon.init();
|
withRows(tCon.writeLinesAcceptor());
|
||||||
withRows(tCon.writeLinesAcceptor());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package io.xpipe.core.source;
|
package io.xpipe.core.source;
|
||||||
|
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
public abstract class TextDataSource<DS extends DataStore> extends DataSource<DS> {
|
public abstract class TextDataSource<DS extends DataStore> extends DataSource<DS> {
|
||||||
|
|
||||||
private static final int MAX_LINE_READ = 1000;
|
private static final int MAX_LINE_READ = 1000;
|
||||||
|
|
|
@ -10,5 +10,11 @@ public interface FileDataStore extends StreamDataStore {
|
||||||
var i = n.lastIndexOf('.');
|
var i = n.lastIndexOf('.');
|
||||||
return Optional.of(i != -1 ? n.substring(0, i) : n);
|
return Optional.of(i != -1 ? n.substring(0, i) : n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean persistent() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
String getFileName();
|
String getFileName();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.xpipe.core.store;
|
package io.xpipe.core.store;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
@ -15,7 +16,6 @@ public class InputStreamDataStore implements StreamDataStore {
|
||||||
|
|
||||||
private final InputStream in;
|
private final InputStream in;
|
||||||
private BufferedInputStream bufferedInputStream;
|
private BufferedInputStream bufferedInputStream;
|
||||||
private boolean opened = false;
|
|
||||||
|
|
||||||
public InputStreamDataStore(InputStream in) {
|
public InputStreamDataStore(InputStream in) {
|
||||||
this.in = in;
|
this.in = in;
|
||||||
|
@ -23,13 +23,84 @@ public class InputStreamDataStore implements StreamDataStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream openInput() throws Exception {
|
public InputStream openInput() throws Exception {
|
||||||
if (opened) {
|
if (bufferedInputStream != null) {
|
||||||
|
bufferedInputStream.reset();
|
||||||
return bufferedInputStream;
|
return bufferedInputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
|
||||||
bufferedInputStream = new BufferedInputStream(in);
|
bufferedInputStream = new BufferedInputStream(in);
|
||||||
return bufferedInputStream;
|
bufferedInputStream.mark(Integer.MAX_VALUE);
|
||||||
|
return new InputStream() {
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return bufferedInputStream.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return bufferedInputStream.read(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
return bufferedInputStream.read(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] readAllBytes() throws IOException {
|
||||||
|
return bufferedInputStream.readAllBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] readNBytes(int len) throws IOException {
|
||||||
|
return bufferedInputStream.readNBytes(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readNBytes(byte[] b, int off, int len) throws IOException {
|
||||||
|
return bufferedInputStream.readNBytes(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
return bufferedInputStream.skip(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skipNBytes(long n) throws IOException {
|
||||||
|
bufferedInputStream.skipNBytes(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return bufferedInputStream.available();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void mark(int readlimit) {
|
||||||
|
bufferedInputStream.mark(readlimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
bufferedInputStream.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return bufferedInputStream.markSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long transferTo(OutputStream out) throws IOException {
|
||||||
|
return bufferedInputStream.transferTo(out);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -52,6 +53,11 @@ public class LocalFileDataStore implements FileDataStore {
|
||||||
return Files.newOutputStream(file);
|
return Files.newOutputStream(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream openAppendingOutput() throws Exception {
|
||||||
|
return Files.newOutputStream(file, StandardOpenOption.APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists() {
|
public boolean exists() {
|
||||||
return Files.exists(file);
|
return Files.exists(file);
|
||||||
|
|
|
@ -45,5 +45,13 @@ public interface StreamDataStore extends DataStore {
|
||||||
throw new UnsupportedOperationException("Can't open store output");
|
throw new UnsupportedOperationException("Can't open store output");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default OutputStream openAppendingOutput() throws Exception {
|
||||||
|
throw new UnsupportedOperationException("Can't open store output");
|
||||||
|
}
|
||||||
|
|
||||||
boolean exists();
|
boolean exists();
|
||||||
|
|
||||||
|
default boolean persistent() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
22
core/src/main/java/io/xpipe/core/store/URLDataStore.java
Normal file
22
core/src/main/java/io/xpipe/core/store/URLDataStore.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package io.xpipe.core.store;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public class URLDataStore implements StreamDataStore {
|
||||||
|
|
||||||
|
URL url;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream openInput() throws Exception {
|
||||||
|
return url.openStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ module io.xpipe.core {
|
||||||
opens io.xpipe.core.data.typed;
|
opens io.xpipe.core.data.typed;
|
||||||
exports io.xpipe.core.config;
|
exports io.xpipe.core.config;
|
||||||
opens io.xpipe.core.config;
|
opens io.xpipe.core.config;
|
||||||
|
exports io.xpipe.core.connection;
|
||||||
|
|
||||||
requires com.fasterxml.jackson.core;
|
requires com.fasterxml.jackson.core;
|
||||||
requires com.fasterxml.jackson.databind;
|
requires com.fasterxml.jackson.databind;
|
||||||
|
|
|
@ -30,7 +30,9 @@ repositories {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compileOnly 'org.junit.jupiter:junit-jupiter-api:5.8.2'
|
||||||
implementation project(':core')
|
implementation project(':core')
|
||||||
|
implementation project(':charsetter')
|
||||||
|
|
||||||
implementation 'io.xpipe:fxcomps:0.1'
|
implementation 'io.xpipe:fxcomps:0.1'
|
||||||
implementation 'com.google.code.gson:gson:2.9.0'
|
implementation 'com.google.code.gson:gson:2.9.0'
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
package io.xpipe.extension;
|
package io.xpipe.extension;
|
||||||
|
|
||||||
import io.xpipe.core.config.ConfigOption;
|
import io.xpipe.charsetter.NewLine;
|
||||||
import io.xpipe.core.config.ConfigOptionSet;
|
import io.xpipe.core.config.ConfigConverter;
|
||||||
|
import io.xpipe.core.config.ConfigParameter;
|
||||||
import io.xpipe.core.source.DataSource;
|
import io.xpipe.core.source.DataSource;
|
||||||
import io.xpipe.core.source.DataSourceType;
|
import io.xpipe.core.source.DataSourceType;
|
||||||
import io.xpipe.core.store.DataStore;
|
import io.xpipe.core.store.DataStore;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -26,9 +28,19 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
||||||
return GeneralType.FILE;
|
return GeneralType.FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getDatabaseProvider() != null) {
|
||||||
|
return GeneralType.DATABASE;
|
||||||
|
}
|
||||||
|
|
||||||
throw new ExtensionException("Provider has no general type");
|
throw new ExtensionException("Provider has no general type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
default T createDefault() {
|
||||||
|
return (T) getSourceClass().getDeclaredConstructors()[0].newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
default boolean supportsConversion(T in, DataSourceType t) {
|
default boolean supportsConversion(T in, DataSourceType t) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +49,7 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
||||||
throw new ExtensionException();
|
throw new ExtensionException();
|
||||||
}
|
}
|
||||||
|
|
||||||
default void init() {
|
default void init() throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
default String i18n(String key) {
|
default String i18n(String key) {
|
||||||
|
@ -48,7 +60,7 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
||||||
return getId() + "." + key;
|
return getId() + "." + key;
|
||||||
}
|
}
|
||||||
|
|
||||||
default Region createConfigOptions(DataStore input, Property<? extends DataSource<?>> source) {
|
default Region createConfigGui(Property<T> source) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,27 +87,32 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
||||||
Map<String, List<String>> getFileExtensions();
|
Map<String, List<String>> getFileExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface DatabaseProvider {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
interface ConfigProvider<T extends DataSource<?>> {
|
interface ConfigProvider<T extends DataSource<?>> {
|
||||||
|
|
||||||
|
@Value
|
||||||
|
static class Parameter {
|
||||||
|
ConfigParameter parameter;
|
||||||
|
Object currentValue;
|
||||||
|
boolean required;
|
||||||
|
}
|
||||||
|
|
||||||
static <T extends DataSource<?>> ConfigProvider<T> empty(List<String> names, Function<DataStore, T> func) {
|
static <T extends DataSource<?>> ConfigProvider<T> empty(List<String> names, Function<DataStore, T> func) {
|
||||||
return new ConfigProvider<>() {
|
return new ConfigProvider<>() {
|
||||||
@Override
|
@Override
|
||||||
public ConfigOptionSet getConfig() {
|
public void applyConfig(T source, Map<ConfigParameter, Object> values) {
|
||||||
return ConfigOptionSet.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T toDescriptor(DataStore store, Map<String, String> values) {
|
public Map<ConfigParameter, Function<T, Object>> toCompleteConfig() {
|
||||||
return func.apply(store);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String> toConfigOptions(T source) {
|
|
||||||
return Map.of();
|
return Map.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<ConfigOption, Function<String, ?>> getConverters() {
|
public Map<ConfigParameter, Object> toRequiredReadConfig(T desc) {
|
||||||
return Map.of();
|
return Map.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,52 +123,29 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigOption
|
ConfigParameter CHARSET = new ConfigParameter(
|
||||||
CHARSET_OPTION = new ConfigOption("Charset", "charset");
|
"charset", ConfigConverter.CHARSET);
|
||||||
Function<String, Charset>
|
|
||||||
CHARSET_CONVERTER = ConfigProvider.charsetConverter();
|
|
||||||
Function<Charset, String>
|
|
||||||
CHARSET_STRING = Charset::name;
|
|
||||||
|
|
||||||
static String booleanName(String name) {
|
public static final ConfigConverter<NewLine> NEW_LINE_CONVERTER = new ConfigConverter<NewLine>() {
|
||||||
return name + " (y/n)";
|
@Override
|
||||||
}
|
protected NewLine fromString(String s) {
|
||||||
|
return NewLine.id(s);
|
||||||
|
}
|
||||||
|
|
||||||
static Function<String, Boolean> booleanConverter() {
|
@Override
|
||||||
return s -> {
|
protected String toString(NewLine value) {
|
||||||
if (s.equalsIgnoreCase("y") || s.equalsIgnoreCase("yes")) {
|
return value.getId();
|
||||||
return true;
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (s.equalsIgnoreCase("n") || s.equalsIgnoreCase("no")) {
|
ConfigParameter NEWLINE = new ConfigParameter(
|
||||||
return false;
|
"newline", NEW_LINE_CONVERTER);
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException("Invalid boolean: " + s);
|
void applyConfig(T source, Map<ConfigParameter, Object> values);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static Function<String, Character> characterConverter() {
|
Map<ConfigParameter, Function<T, Object>> toCompleteConfig();
|
||||||
return s -> {
|
|
||||||
if (s.length() != 1) {
|
|
||||||
throw new IllegalArgumentException("Invalid character: " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.toCharArray()[0];
|
Map<ConfigParameter, Object> toRequiredReadConfig(T desc);
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static Function<String, Charset> charsetConverter() {
|
|
||||||
return Charset::forName;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigOptionSet getConfig();
|
|
||||||
|
|
||||||
T toDescriptor(DataStore store, Map<String, String> values);
|
|
||||||
|
|
||||||
Map<String, String> toConfigOptions(T desc);
|
|
||||||
|
|
||||||
Map<ConfigOption, Function<String, ?>> getConverters();
|
|
||||||
|
|
||||||
List<String> getPossibleNames();
|
List<String> getPossibleNames();
|
||||||
}
|
}
|
||||||
|
@ -190,6 +184,10 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default DatabaseProvider getDatabaseProvider() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
default boolean hasDirectoryProvider() {
|
default boolean hasDirectoryProvider() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -206,16 +204,16 @@ public interface DataSourceProvider<T extends DataSource<?>> {
|
||||||
* Attempt to create a useful data source descriptor from a data store.
|
* Attempt to create a useful data source descriptor from a data store.
|
||||||
* The result does not need to be always right, it should only reflect the best effort.
|
* The result does not need to be always right, it should only reflect the best effort.
|
||||||
*/
|
*/
|
||||||
T createDefaultDescriptor(DataStore input) throws Exception;
|
T createDefaultSource(DataStore input) throws Exception;
|
||||||
|
|
||||||
default T createDefaultWriteDescriptor(DataStore input) throws Exception {
|
default T createDefaultWriteSource(DataStore input) throws Exception {
|
||||||
return createDefaultDescriptor(input);
|
return createDefaultSource(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
default Class<T> getDescriptorClass() {
|
default Class<T> getSourceClass() {
|
||||||
return (Class<T>) Arrays.stream(getClass().getDeclaredClasses())
|
return (Class<T>) Arrays.stream(getClass().getDeclaredClasses())
|
||||||
.filter(c -> c.getName().endsWith("Descriptor")).findFirst()
|
.filter(c -> c.getName().endsWith("Source")).findFirst()
|
||||||
.orElseThrow(() -> new AssertionError("Descriptor class not found"));
|
.orElseThrow(() -> new ExtensionException("Descriptor class not found for " + getId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.xpipe.extension;
|
||||||
import io.xpipe.core.source.*;
|
import io.xpipe.core.source.*;
|
||||||
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 lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -18,7 +19,13 @@ public class DataSourceProviders {
|
||||||
if (ALL == null) {
|
if (ALL == null) {
|
||||||
ALL = ServiceLoader.load(layer, DataSourceProvider.class).stream()
|
ALL = ServiceLoader.load(layer, DataSourceProvider.class).stream()
|
||||||
.map(p -> (DataSourceProvider<?>) p.get()).collect(Collectors.toSet());
|
.map(p -> (DataSourceProvider<?>) p.get()).collect(Collectors.toSet());
|
||||||
ALL.forEach(DataSourceProvider::init);
|
ALL.forEach(p -> {
|
||||||
|
try {
|
||||||
|
p.init();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ErrorEvent.fromThrowable(e).handle();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +48,7 @@ public class DataSourceProviders {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static StructureDataSource<LocalFileDataStore> createLocalStructureDescriptor(DataStore store) {
|
public static StructureDataSource<LocalFileDataStore> createLocalStructureDescriptor(DataStore store) {
|
||||||
return (StructureDataSource<LocalFileDataStore>)
|
return (StructureDataSource<LocalFileDataStore>)
|
||||||
DataSourceProviders.byId("xpbs").getDescriptorClass()
|
DataSourceProviders.byId("xpbs").getSourceClass()
|
||||||
.getDeclaredConstructors()[0].newInstance(store);
|
.getDeclaredConstructors()[0].newInstance(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +56,7 @@ public class DataSourceProviders {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static RawDataSource<LocalFileDataStore> createLocalRawDescriptor(DataStore store) {
|
public static RawDataSource<LocalFileDataStore> createLocalRawDescriptor(DataStore store) {
|
||||||
return (RawDataSource<LocalFileDataStore>)
|
return (RawDataSource<LocalFileDataStore>)
|
||||||
DataSourceProviders.byId("binary").getDescriptorClass()
|
DataSourceProviders.byId("binary").getSourceClass()
|
||||||
.getDeclaredConstructors()[0].newInstance(store);
|
.getDeclaredConstructors()[0].newInstance(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +64,7 @@ public class DataSourceProviders {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static RawDataSource<LocalFileDataStore> createLocalCollectionDescriptor(DataStore store) {
|
public static RawDataSource<LocalFileDataStore> createLocalCollectionDescriptor(DataStore store) {
|
||||||
return (RawDataSource<LocalFileDataStore>)
|
return (RawDataSource<LocalFileDataStore>)
|
||||||
DataSourceProviders.byId("br").getDescriptorClass()
|
DataSourceProviders.byId("br").getSourceClass()
|
||||||
.getDeclaredConstructors()[0].newInstance(store);
|
.getDeclaredConstructors()[0].newInstance(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +72,7 @@ public class DataSourceProviders {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static TextDataSource<LocalFileDataStore> createLocalTextDescriptor(DataStore store) {
|
public static TextDataSource<LocalFileDataStore> createLocalTextDescriptor(DataStore store) {
|
||||||
return (TextDataSource<LocalFileDataStore>)
|
return (TextDataSource<LocalFileDataStore>)
|
||||||
DataSourceProviders.byId("text").getDescriptorClass()
|
DataSourceProviders.byId("text").getSourceClass()
|
||||||
.getDeclaredConstructors()[0].newInstance(store);
|
.getDeclaredConstructors()[0].newInstance(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +80,7 @@ public class DataSourceProviders {
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static TableDataSource<LocalFileDataStore> createLocalTableDescriptor(DataStore store) {
|
public static TableDataSource<LocalFileDataStore> createLocalTableDescriptor(DataStore store) {
|
||||||
return (TableDataSource<LocalFileDataStore>)
|
return (TableDataSource<LocalFileDataStore>)
|
||||||
DataSourceProviders.byId("xpbt").getDescriptorClass()
|
DataSourceProviders.byId("xpbt").getSourceClass()
|
||||||
.getDeclaredConstructors()[0].newInstance(store);
|
.getDeclaredConstructors()[0].newInstance(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +101,7 @@ public class DataSourceProviders {
|
||||||
throw new IllegalStateException("Not initialized");
|
throw new IllegalStateException("Not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (T) ALL.stream().filter(d -> d.getDescriptorClass().equals(c)).findAny()
|
return (T) ALL.stream().filter(d -> d.getSourceClass().equals(c)).findAny()
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Provider for " + c.getSimpleName() + " not found"));
|
.orElseThrow(() -> new IllegalArgumentException("Provider for " + c.getSimpleName() + " not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package io.xpipe.extension;
|
||||||
|
|
||||||
|
import io.xpipe.charsetter.Charsetter;
|
||||||
|
import io.xpipe.charsetter.CharsetterContext;
|
||||||
|
import io.xpipe.core.util.JacksonHelper;
|
||||||
|
import io.xpipe.extension.util.ModuleHelper;
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
|
||||||
|
public class ExtensionTest {
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void setup() throws Exception {
|
||||||
|
var mod = ModuleLayer.boot().modules().stream()
|
||||||
|
.filter(m -> m.getName().contains(".test"))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
var e = ModuleHelper.getEveryoneModule();
|
||||||
|
for (var pkg : mod.getPackages()) {
|
||||||
|
ModuleHelper.exportAndOpen(pkg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
var extMod = ModuleLayer.boot().modules().stream()
|
||||||
|
.filter(m -> m.getName().equals(mod.getName().replace(".test", "")))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
for (var pkg : extMod.getPackages()) {
|
||||||
|
ModuleHelper.exportAndOpen(pkg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
JacksonHelper.initClassBased();
|
||||||
|
Charsetter.init(CharsetterContext.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
public static void teardown() throws Exception {
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,14 +10,14 @@ public interface UniformDataSourceProvider<T extends DataSource<?>> extends Data
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default ConfigProvider<T> getConfigProvider() {
|
default ConfigProvider<T> getConfigProvider() {
|
||||||
return ConfigProvider.empty(List.of(getId()), this::createDefaultDescriptor);
|
return ConfigProvider.empty(List.of(getId()), this::createDefaultSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
default T createDefaultDescriptor(DataStore input) {
|
default T createDefaultSource(DataStore input) {
|
||||||
try {
|
try {
|
||||||
return (T) getDescriptorClass().getDeclaredConstructors()[0].newInstance(input);
|
return (T) getSourceClass().getDeclaredConstructors()[0].newInstance(input);
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,21 +4,20 @@ import io.xpipe.fxcomps.Comp;
|
||||||
import io.xpipe.fxcomps.CompStructure;
|
import io.xpipe.fxcomps.CompStructure;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import org.apache.commons.collections4.BidiMap;
|
import org.apache.commons.collections4.BidiMap;
|
||||||
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class CharChoiceComp extends Comp<CompStructure<HBox>> {
|
public class CharChoiceComp extends Comp<CompStructure<HBox>> {
|
||||||
|
|
||||||
private final Property<Character> value;
|
private final Property<Character> value;
|
||||||
private final Property<Character> charChoiceValue;
|
private final Property<Character> charChoiceValue;
|
||||||
private final BidiMap<Character, Supplier<String>> range;
|
private final BidiMap<Character, ObservableValue<String>> range;
|
||||||
private final Supplier<String> customName;
|
private final ObservableValue<String> customName;
|
||||||
|
|
||||||
public CharChoiceComp(Property<Character> value, BidiMap<Character, Supplier<String>> range, Supplier<String> customName) {
|
public CharChoiceComp(Property<Character> value, BidiMap<Character, ObservableValue<String>> range, ObservableValue<String> customName) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.range = range;
|
this.range = range;
|
||||||
this.customName = customName;
|
this.customName = customName;
|
||||||
|
|
|
@ -4,12 +4,13 @@ import io.xpipe.fxcomps.Comp;
|
||||||
import io.xpipe.fxcomps.CompStructure;
|
import io.xpipe.fxcomps.CompStructure;
|
||||||
import io.xpipe.fxcomps.comp.ReplacementComp;
|
import io.xpipe.fxcomps.comp.ReplacementComp;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class CharsetChoiceComp extends ReplacementComp<CompStructure<ComboBox<Charset>>> {
|
public class CharsetChoiceComp extends ReplacementComp<CompStructure<ComboBox<Charset>>> {
|
||||||
|
|
||||||
|
@ -21,9 +22,9 @@ public class CharsetChoiceComp extends ReplacementComp<CompStructure<ComboBox<Ch
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Comp<CompStructure<ComboBox<Charset>>> createComp() {
|
protected Comp<CompStructure<ComboBox<Charset>>> createComp() {
|
||||||
var map = new LinkedHashMap<Charset, Supplier<String>>();
|
var map = new LinkedHashMap<Charset, ObservableValue<String>>();
|
||||||
for (var e : Charset.availableCharsets().entrySet()) {
|
for (var e : Charset.availableCharsets().entrySet()) {
|
||||||
map.put(e.getValue(), e::getKey);
|
map.put(e.getValue(), new SimpleStringProperty(e.getKey()));
|
||||||
}
|
}
|
||||||
return new ChoiceComp<>(charset, new DualLinkedHashBidiMap<>(map));
|
return new ChoiceComp<>(charset, new DualLinkedHashBidiMap<>(map));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,18 @@ import io.xpipe.fxcomps.Comp;
|
||||||
import io.xpipe.fxcomps.CompStructure;
|
import io.xpipe.fxcomps.CompStructure;
|
||||||
import io.xpipe.fxcomps.util.PlatformUtil;
|
import io.xpipe.fxcomps.util.PlatformUtil;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
import org.apache.commons.collections4.BidiMap;
|
import org.apache.commons.collections4.BidiMap;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ChoiceComp<T> extends Comp<CompStructure<ComboBox<T>>> {
|
public class ChoiceComp<T> extends Comp<CompStructure<ComboBox<T>>> {
|
||||||
|
|
||||||
private final Property<T> value;
|
private final Property<T> value;
|
||||||
private final BidiMap<T, Supplier<String>> range;
|
private final BidiMap<T, ObservableValue<String>> range;
|
||||||
|
|
||||||
public ChoiceComp(Property<T> value, BidiMap<T, Supplier<String>> range) {
|
public ChoiceComp(Property<T> value, BidiMap<T, ObservableValue<String>> range) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.range = range;
|
this.range = range;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +27,11 @@ public class ChoiceComp<T> extends Comp<CompStructure<ComboBox<T>>> {
|
||||||
cb.setConverter(new StringConverter<>() {
|
cb.setConverter(new StringConverter<>() {
|
||||||
@Override
|
@Override
|
||||||
public String toString(T object) {
|
public String toString(T object) {
|
||||||
return range.get(object).get();
|
if (object == null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
return range.get(object).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,17 +1,24 @@
|
||||||
package io.xpipe.extension.comp;
|
package io.xpipe.extension.comp;
|
||||||
|
|
||||||
|
import io.xpipe.charsetter.NewLine;
|
||||||
import io.xpipe.core.source.DataSource;
|
import io.xpipe.core.source.DataSource;
|
||||||
|
import io.xpipe.extension.I18n;
|
||||||
import io.xpipe.fxcomps.Comp;
|
import io.xpipe.fxcomps.Comp;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import org.apache.commons.collections4.bidimap.DualLinkedHashBidiMap;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Supplier;
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class DynamicOptionsBuilder<T extends DataSource<?>> {
|
public class DynamicOptionsBuilder<T extends DataSource<?>> {
|
||||||
|
|
||||||
|
@ -22,12 +29,54 @@ public class DynamicOptionsBuilder<T extends DataSource<?>> {
|
||||||
var comp = new TextField();
|
var comp = new TextField();
|
||||||
comp.textProperty().bindBidirectional(prop);
|
comp.textProperty().bindBidirectional(prop);
|
||||||
entries.add(new DynamicOptionsComp.Entry(name, Comp.of(() -> comp)));
|
entries.add(new DynamicOptionsComp.Entry(name, Comp.of(() -> comp)));
|
||||||
|
props.add(prop);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Region build(Supplier<T> creator, Property<T> toBind) {
|
public DynamicOptionsBuilder<T> addNewLine(Property<NewLine> prop) {
|
||||||
var bind = Bindings.createObjectBinding(() -> creator.get(), props.toArray(Observable[]::new));
|
var map = new LinkedHashMap<NewLine, ObservableValue<String>>();
|
||||||
toBind.bind(bind);
|
for (var e : NewLine.values()) {
|
||||||
|
map.put(e, new SimpleStringProperty(e.getId()));
|
||||||
|
}
|
||||||
|
var comp = new ChoiceComp<>(prop, new DualLinkedHashBidiMap<>(map));
|
||||||
|
entries.add(new DynamicOptionsComp.Entry(I18n.observable("newLine"), comp));
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicOptionsBuilder<T> addCharacter(Property<Character> prop, ObservableValue<String> name, Map<Character, ObservableValue<String>> names) {
|
||||||
|
var comp = new CharChoiceComp(prop, new DualLinkedHashBidiMap<>(names), null);
|
||||||
|
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicOptionsBuilder<T> addCharacter(Property<Character> prop, ObservableValue<String> name, Map<Character, ObservableValue<String>> names, ObservableValue<String> customName) {
|
||||||
|
var comp = new CharChoiceComp(prop, new DualLinkedHashBidiMap<>(names), customName);
|
||||||
|
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <V> DynamicOptionsBuilder<T> addToggle(Property<V> prop, ObservableValue<String> name, Map<V, ObservableValue<String>> names) {
|
||||||
|
var comp = new ToggleGroupComp<>(prop, new DualLinkedHashBidiMap<>(names));
|
||||||
|
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicOptionsBuilder<T> addCharset(Property<Charset> prop) {
|
||||||
|
var comp = new CharsetChoiceComp(prop);
|
||||||
|
entries.add(new DynamicOptionsComp.Entry(I18n.observable("charset"), comp));
|
||||||
|
props.add(prop);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Region build(Function<T, T> creator, Property<T> toBind) {
|
||||||
|
var bind = Bindings.createObjectBinding(() -> creator.apply(toBind.getValue()), props.toArray(Observable[]::new));
|
||||||
|
bind.addListener((c,o, n) -> {
|
||||||
|
toBind.setValue(n);
|
||||||
|
});
|
||||||
return new DynamicOptionsComp(entries).createRegion();
|
return new DynamicOptionsComp(entries).createRegion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,23 @@ import io.xpipe.fxcomps.Comp;
|
||||||
import io.xpipe.fxcomps.CompStructure;
|
import io.xpipe.fxcomps.CompStructure;
|
||||||
import io.xpipe.fxcomps.util.PlatformUtil;
|
import io.xpipe.fxcomps.util.PlatformUtil;
|
||||||
import javafx.beans.property.Property;
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.scene.control.ToggleButton;
|
import javafx.scene.control.ToggleButton;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import org.apache.commons.collections4.BidiMap;
|
import org.apache.commons.collections4.BidiMap;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ToggleGroupComp<T> extends Comp<CompStructure<HBox>> {
|
public class ToggleGroupComp<T> extends Comp<CompStructure<HBox>> {
|
||||||
|
|
||||||
private final Property<T> value;
|
private final Property<T> value;
|
||||||
private final BidiMap<T, Supplier<String>> range;
|
private final BidiMap<T, ObservableValue<String>> range;
|
||||||
|
|
||||||
public ToggleGroupComp(Property<T> value, BidiMap<T, Supplier<String>> range) {
|
public ToggleGroupComp(Property<T> value, BidiMap<T, ObservableValue<String>> range) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.range = range;
|
this.range = range;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BidiMap<T, Supplier<String>> getRange() {
|
public BidiMap<T, ObservableValue<String>> getRange() {
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +30,7 @@ public class ToggleGroupComp<T> extends Comp<CompStructure<HBox>> {
|
||||||
box.getStyleClass().add("toggle-group-comp");
|
box.getStyleClass().add("toggle-group-comp");
|
||||||
ToggleGroup group = new ToggleGroup();
|
ToggleGroup group = new ToggleGroup();
|
||||||
for (var entry : range.entrySet()) {
|
for (var entry : range.entrySet()) {
|
||||||
var b = new ToggleButton(entry.getValue().get());
|
var b = new ToggleButton(entry.getValue().getValue());
|
||||||
b.setOnAction(e -> {
|
b.setOnAction(e -> {
|
||||||
value.setValue(entry.getKey());
|
value.setValue(entry.getKey());
|
||||||
e.consume();
|
e.consume();
|
||||||
|
|
|
@ -15,7 +15,11 @@ public abstract class EventHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(TrackEvent te) {
|
public void handle(TrackEvent te) {
|
||||||
LoggerFactory.getLogger(te.getCategory()).info(te.getMessage());
|
var cat = te.getCategory();
|
||||||
|
if (cat == null) {
|
||||||
|
cat = "log";
|
||||||
|
}
|
||||||
|
LoggerFactory.getLogger(cat).info(te.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package io.xpipe.extension.util;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class ModuleHelper {
|
||||||
|
|
||||||
|
public static void exportAndOpen(String modName) {
|
||||||
|
var mod = ModuleLayer.boot().modules().stream()
|
||||||
|
.filter(m -> m.getName().equalsIgnoreCase(modName))
|
||||||
|
.findFirst().orElseThrow();
|
||||||
|
var e = getEveryoneModule();
|
||||||
|
for (var pkg : mod.getPackages()) {
|
||||||
|
exportAndOpen(pkg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static Module getEveryoneModule() {
|
||||||
|
Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
|
||||||
|
getDeclaredFields0.setAccessible(true);
|
||||||
|
Field[] fields = (Field[]) getDeclaredFields0.invoke(Module.class, false);
|
||||||
|
Field modifiers = null;
|
||||||
|
for (Field each : fields) {
|
||||||
|
if ("EVERYONE_MODULE".equals(each.getName())) {
|
||||||
|
modifiers = each;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modifiers.setAccessible(true);
|
||||||
|
return (Module) modifiers.get(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void exportAndOpen(String pkg, Module mod) {
|
||||||
|
if (mod.isExported(pkg) && mod.isOpen(pkg)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredMethods0", boolean.class);
|
||||||
|
getDeclaredFields0.setAccessible(true);
|
||||||
|
Method[] fields = (Method[]) getDeclaredFields0.invoke(Module.class, false);
|
||||||
|
Method modifiers = null;
|
||||||
|
for (Method each : fields) {
|
||||||
|
if ("implAddExportsOrOpens".equals(each.getName())) {
|
||||||
|
modifiers = each;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modifiers.setAccessible(true);
|
||||||
|
|
||||||
|
var e = getEveryoneModule();
|
||||||
|
modifiers.invoke(mod, pkg, e, false, true);
|
||||||
|
modifiers.invoke(mod, pkg, e, true, true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,8 @@ module io.xpipe.extension {
|
||||||
requires org.reactfx;
|
requires org.reactfx;
|
||||||
requires org.kordamp.ikonli.javafx;
|
requires org.kordamp.ikonli.javafx;
|
||||||
requires com.fasterxml.jackson.databind;
|
requires com.fasterxml.jackson.databind;
|
||||||
|
requires static org.junit.jupiter.api;
|
||||||
|
requires io.xpipe.charsetter;
|
||||||
|
|
||||||
uses DataSourceProvider;
|
uses DataSourceProvider;
|
||||||
uses SupportedApplicationProvider;
|
uses SupportedApplicationProvider;
|
||||||
|
|
Loading…
Reference in a new issue