From 8d95f451bbd56e049cb223eb29edc8d3357d6450 Mon Sep 17 00:00:00 2001 From: Christopher Schnick Date: Tue, 11 Oct 2022 12:23:37 +0200 Subject: [PATCH] Rework --- .../java/io/xpipe/beacon/BeaconClient.java | 14 +- .../io/xpipe/core/data/node/ArrayNode.java | 4 +- ...bleValueNode.java => SimpleValueNode.java} | 8 +- .../io/xpipe/core/data/node/TupleNode.java | 18 ++- .../io/xpipe/core/data/node/ValueNode.java | 9 +- .../core/impl/BatchTableWriteConnection.java | 22 ++-- .../xpipe/core/impl/XpbtReadConnection.java | 5 +- .../xpipe/core/impl/XpbtWriteConnection.java | 4 +- .../java/io/xpipe/core/source/DataSource.java | 37 +----- .../io/xpipe/core/source/TableDataSource.java | 6 + .../core/store/CollectionEntryDataStore.java | 2 +- .../java/io/xpipe/core/store/DataStore.java | 4 +- .../java/io/xpipe/core/store/FileStore.java | 18 ++- .../io/xpipe/core/store/InMemoryStore.java | 23 ++-- .../java/io/xpipe/core/store/LocalStore.java | 7 +- .../io/xpipe/core/store/MachineFileStore.java | 5 - .../java/io/xpipe/core/store/NamedStore.java | 2 +- .../io/xpipe/core/store/ProcessControl.java | 4 +- .../java/io/xpipe/core/store/ShellStore.java | 46 +++---- .../java/io/xpipe/core/store/ShellTypes.java | 6 +- .../xpipe/core/store/StandardShellStore.java | 19 ++- .../io/xpipe/core/store/StdinDataStore.java | 9 +- .../io/xpipe/core/store/StdoutDataStore.java | 9 +- ...{JacksonHelper.java => JacksonMapper.java} | 39 +++--- .../io/xpipe/core/util/JacksonizedValue.java | 38 ++++++ .../java/io/xpipe/core/util/SecretValue.java | 7 +- .../xpipe/extension/DataSourceProvider.java | 11 +- .../io/xpipe/extension/DataStoreProvider.java | 11 ++ .../xpipe/extension/DataStoreProviders.java | 13 +- .../extension/comp/FancyTooltipAugment.java | 5 +- .../io/xpipe/extension/comp/FilterComp.java | 2 - .../java/io/xpipe/extension/comp/SvgComp.java | 124 ++++++++++++++++++ .../extension/comp/WriteModeChoiceComp.java | 35 ++++- .../extension/prefs/PrefsChoiceValue.java | 4 - .../extension/util/CustomComboBoxBuilder.java | 2 - .../xpipe/extension/util/PrettyListView.java | 74 +++++++++++ .../io/xpipe/extension/util/Validators.java | 10 +- .../io/xpipe/extension/util/XPipeDaemon.java | 4 +- extension/src/main/java/module-info.java | 1 + .../resources/lang/translations_en.properties | 1 + 40 files changed, 471 insertions(+), 191 deletions(-) rename core/src/main/java/io/xpipe/core/data/node/{SimpleImmutableValueNode.java => SimpleValueNode.java} (77%) rename core/src/main/java/io/xpipe/core/util/{JacksonHelper.java => JacksonMapper.java} (70%) create mode 100644 core/src/main/java/io/xpipe/core/util/JacksonizedValue.java create mode 100644 extension/src/main/java/io/xpipe/extension/comp/SvgComp.java diff --git a/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java b/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java index 49611953f..7e9e9bf42 100644 --- a/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java +++ b/beacon/src/main/java/io/xpipe/beacon/BeaconClient.java @@ -9,7 +9,7 @@ import com.fasterxml.jackson.databind.node.TextNode; import io.xpipe.beacon.exchange.MessageExchanges; import io.xpipe.beacon.exchange.data.ClientErrorMessage; import io.xpipe.beacon.exchange.data.ServerErrorMessage; -import io.xpipe.core.util.JacksonHelper; +import io.xpipe.core.util.JacksonMapper; import java.io.*; import java.net.InetAddress; @@ -96,7 +96,7 @@ public class BeaconClient implements AutoCloseable { } public void sendRequest(T req) throws ClientException, ConnectorException { - ObjectNode json = JacksonHelper.newMapper().valueToTree(req); + ObjectNode json = JacksonMapper.newMapper().valueToTree(req); var prov = MessageExchanges.byRequest(req); if (prov.isEmpty()) { throw new ClientException("Unknown request class " + req.getClass()); @@ -113,7 +113,7 @@ public class BeaconClient implements AutoCloseable { } var writer = new StringWriter(); - var mapper = JacksonHelper.newMapper(); + var mapper = JacksonMapper.newMapper(); try (JsonGenerator g = mapper.createGenerator(writer).setPrettyPrinter(new DefaultPrettyPrinter())) { g.writeTree(msg); } catch (IOException ex) { @@ -136,7 +136,7 @@ public class BeaconClient implements AutoCloseable { public T receiveResponse() throws ConnectorException, ClientException, ServerException { JsonNode node; try (InputStream blockIn = BeaconFormat.readBlocks(in)) { - node = JacksonHelper.newMapper().readTree(blockIn); + node = JacksonMapper.newMapper().readTree(blockIn); } catch (SocketException ex) { throw new ConnectorException("Connection to xpipe daemon closed unexpectedly", ex); } catch (IOException ex) { @@ -172,7 +172,7 @@ public class BeaconClient implements AutoCloseable { } try { - var reader = JacksonHelper.newMapper().readerFor(ClientErrorMessage.class); + var reader = JacksonMapper.newMapper().readerFor(ClientErrorMessage.class); return Optional.of(reader.readValue(content)); } catch (IOException ex) { throw new ConnectorException("Couldn't parse client error message", ex); @@ -186,7 +186,7 @@ public class BeaconClient implements AutoCloseable { } try { - var reader = JacksonHelper.newMapper().readerFor(ServerErrorMessage.class); + var reader = JacksonMapper.newMapper().readerFor(ServerErrorMessage.class); return Optional.of(reader.readValue(content)); } catch (IOException ex) { throw new ConnectorException("Couldn't parse server error message", ex); @@ -212,7 +212,7 @@ public class BeaconClient implements AutoCloseable { } try { - var reader = JacksonHelper.newMapper().readerFor(prov.get().getResponseClass()); + var reader = JacksonMapper.newMapper().readerFor(prov.get().getResponseClass()); return reader.readValue(content); } catch (IOException ex) { throw new ConnectorException("Couldn't parse response", ex); diff --git a/core/src/main/java/io/xpipe/core/data/node/ArrayNode.java b/core/src/main/java/io/xpipe/core/data/node/ArrayNode.java index 9662ba93b..60ace970b 100644 --- a/core/src/main/java/io/xpipe/core/data/node/ArrayNode.java +++ b/core/src/main/java/io/xpipe/core/data/node/ArrayNode.java @@ -33,8 +33,10 @@ public abstract class ArrayNode extends DataStructureNode { } var toReturn = getNodes().equals(that.getNodes()) && Objects.equals(getMetaAttributes(), that.getMetaAttributes()); + + // Useful for debugging if (toReturn == false) { - throw new AssertionError(); + return false; } return toReturn; } diff --git a/core/src/main/java/io/xpipe/core/data/node/SimpleImmutableValueNode.java b/core/src/main/java/io/xpipe/core/data/node/SimpleValueNode.java similarity index 77% rename from core/src/main/java/io/xpipe/core/data/node/SimpleImmutableValueNode.java rename to core/src/main/java/io/xpipe/core/data/node/SimpleValueNode.java index 082e92e0b..ff23d4386 100644 --- a/core/src/main/java/io/xpipe/core/data/node/SimpleImmutableValueNode.java +++ b/core/src/main/java/io/xpipe/core/data/node/SimpleValueNode.java @@ -4,11 +4,11 @@ import lombok.NonNull; import java.nio.charset.StandardCharsets; -public class SimpleImmutableValueNode extends ValueNode { +public class SimpleValueNode extends ValueNode { private final byte @NonNull [] data; - SimpleImmutableValueNode(byte @NonNull [] data) { + SimpleValueNode(byte @NonNull [] data) { this.data = data; } @@ -18,10 +18,6 @@ public class SimpleImmutableValueNode extends ValueNode { @Override public final String asString() { - if (getRawData().length == 0 && !hasMetaAttribute(IS_TEXT)) { - return "null"; - } - return new String(getRawData(), StandardCharsets.UTF_8); } diff --git a/core/src/main/java/io/xpipe/core/data/node/TupleNode.java b/core/src/main/java/io/xpipe/core/data/node/TupleNode.java index c47ce3553..c6f1077d5 100644 --- a/core/src/main/java/io/xpipe/core/data/node/TupleNode.java +++ b/core/src/main/java/io/xpipe/core/data/node/TupleNode.java @@ -59,11 +59,18 @@ public abstract class TupleNode extends DataStructureNode { @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TupleNode that)) return false; - var toReturn = getKeyNames().equals(that.getKeyNames()) && getNodes().equals(that.getNodes()) && Objects.equals(getMetaAttributes(), that.getMetaAttributes()); + if (this == o) { + return true; + } + if (!(o instanceof TupleNode that)) { + return false; + } + var toReturn = getKeyNames().equals(that.getKeyNames()) && getNodes().equals(that.getNodes()) && + Objects.equals(getMetaAttributes(), that.getMetaAttributes()); + + // Useful for debugging if (toReturn == false) { - throw new AssertionError(); + return false; } return toReturn; } @@ -93,7 +100,8 @@ public abstract class TupleNode extends DataStructureNode { boolean hasKeys = entries.stream().anyMatch(kv -> kv.key() != null); return hasKeys ? TupleNode.of( entries.stream().map(KeyValue::key).toList(), - entries.stream().map(KeyValue::value).toList()) : + entries.stream().map(KeyValue::value).toList() + ) : TupleNode.of(entries.stream().map(KeyValue::value).toList()); } } diff --git a/core/src/main/java/io/xpipe/core/data/node/ValueNode.java b/core/src/main/java/io/xpipe/core/data/node/ValueNode.java index da06d9c5b..89dff87c6 100644 --- a/core/src/main/java/io/xpipe/core/data/node/ValueNode.java +++ b/core/src/main/java/io/xpipe/core/data/node/ValueNode.java @@ -24,9 +24,12 @@ public abstract class ValueNode extends DataStructureNode { return false; } var toReturn = Arrays.equals(getRawData(), that.getRawData()) && Objects.equals(getMetaAttributes(), that.getMetaAttributes()); + + // Useful for debugging if (toReturn == false) { - throw new AssertionError(); + return false; } + return toReturn; } @@ -36,7 +39,7 @@ public abstract class ValueNode extends DataStructureNode { } public static ValueNode nullValue() { - return new SimpleImmutableValueNode(new byte[0]).tag(IS_NULL).asValue(); + return new SimpleValueNode(new byte[0]).tag(IS_NULL).asValue(); } public static ValueNode of(byte[] data) { @@ -44,7 +47,7 @@ public abstract class ValueNode extends DataStructureNode { return nullValue(); } - return new SimpleImmutableValueNode(data); + return new SimpleValueNode(data); } public static ValueNode ofBytes(byte[] data) { diff --git a/core/src/main/java/io/xpipe/core/impl/BatchTableWriteConnection.java b/core/src/main/java/io/xpipe/core/impl/BatchTableWriteConnection.java index 2282077ac..4a9caf615 100644 --- a/core/src/main/java/io/xpipe/core/impl/BatchTableWriteConnection.java +++ b/core/src/main/java/io/xpipe/core/impl/BatchTableWriteConnection.java @@ -11,16 +11,19 @@ import java.util.List; public abstract class BatchTableWriteConnection implements TableWriteConnection { - public static final int BATCH_SIZE = 2000; + public static final int DEFAULT_BATCH_SIZE = 2000; + protected final int batchSize = DEFAULT_BATCH_SIZE; private final List batch = new ArrayList<>(); @Override public final DataStructureNodeAcceptor writeLinesAcceptor() { return node -> { - if (batch.size() < BATCH_SIZE) { + if (batch.size() < batchSize) { batch.add(node); - return true; + if (batch.size() < batchSize) { + return true; + } } var array = ArrayNode.of(batch); @@ -32,12 +35,15 @@ public abstract class BatchTableWriteConnection implements TableWriteConnection @Override public final void close() throws Exception { - if (batch.size() > 0) { - var array = ArrayNode.of(batch); - var returned = writeBatchLinesAcceptor().accept(array); - batch.clear(); + try { + if (batch.size() > 0) { + var array = ArrayNode.of(batch); + var returned = writeBatchLinesAcceptor().accept(array); + batch.clear(); + } + } finally { + onClose(); } - onClose(); } protected abstract void onClose() throws Exception; diff --git a/core/src/main/java/io/xpipe/core/impl/XpbtReadConnection.java b/core/src/main/java/io/xpipe/core/impl/XpbtReadConnection.java index 028858b31..663916c98 100644 --- a/core/src/main/java/io/xpipe/core/impl/XpbtReadConnection.java +++ b/core/src/main/java/io/xpipe/core/impl/XpbtReadConnection.java @@ -1,6 +1,5 @@ package io.xpipe.core.impl; -import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; import io.xpipe.core.data.node.DataStructureNodeAcceptor; @@ -10,7 +9,7 @@ import io.xpipe.core.data.typed.TypedDataStreamParser; import io.xpipe.core.data.typed.TypedDataStructureNodeReader; import io.xpipe.core.source.TableReadConnection; import io.xpipe.core.store.StreamDataStore; -import io.xpipe.core.util.JacksonHelper; +import io.xpipe.core.util.JacksonMapper; import java.io.BufferedReader; import java.io.InputStream; @@ -35,7 +34,7 @@ public class XpbtReadConnection implements TableReadConnection { var headerLength = header.getBytes(StandardCharsets.UTF_8).length; this.inputStream.skip(headerLength + 1); - List names = JacksonHelper.newMapper() + List names = JacksonMapper.newMapper() .disable(JsonParser.Feature.AUTO_CLOSE_SOURCE) .readerFor(new TypeReference>() { }).readValue(header); diff --git a/core/src/main/java/io/xpipe/core/impl/XpbtWriteConnection.java b/core/src/main/java/io/xpipe/core/impl/XpbtWriteConnection.java index b638ddc3a..dd254aceb 100644 --- a/core/src/main/java/io/xpipe/core/impl/XpbtWriteConnection.java +++ b/core/src/main/java/io/xpipe/core/impl/XpbtWriteConnection.java @@ -9,7 +9,7 @@ import io.xpipe.core.data.type.TupleType; import io.xpipe.core.data.typed.TypedDataStreamWriter; import io.xpipe.core.source.TableWriteConnection; import io.xpipe.core.store.StreamDataStore; -import io.xpipe.core.util.JacksonHelper; +import io.xpipe.core.util.JacksonMapper; import java.io.IOException; import java.io.OutputStream; @@ -58,7 +58,7 @@ public class XpbtWriteConnection implements TableWriteConnection { try (JsonGenerator g = f.createGenerator(writer) .disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET) .setPrettyPrinter(new DefaultPrettyPrinter())) { - JacksonHelper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET) + JacksonMapper.newMapper().disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET) .writeValue(g, tupleNode.getKeyNames()); writer.append("\n"); } diff --git a/core/src/main/java/io/xpipe/core/source/DataSource.java b/core/src/main/java/io/xpipe/core/source/DataSource.java index 2c6a9ed8f..66de27901 100644 --- a/core/src/main/java/io/xpipe/core/source/DataSource.java +++ b/core/src/main/java/io/xpipe/core/source/DataSource.java @@ -8,7 +8,8 @@ import io.xpipe.core.impl.TextSource; import io.xpipe.core.impl.XpbtSource; import io.xpipe.core.store.DataFlow; import io.xpipe.core.store.DataStore; -import io.xpipe.core.util.JacksonHelper; +import io.xpipe.core.util.JacksonMapper; +import io.xpipe.core.util.JacksonizedValue; import lombok.SneakyThrows; import lombok.experimental.SuperBuilder; @@ -25,7 +26,7 @@ import java.util.Optional; use = JsonTypeInfo.Id.NAME, property = "type" ) -public abstract class DataSource { +public abstract class DataSource extends JacksonizedValue { public static DataSource createInternalDataSource(DataSourceType t, DataStore store) { @@ -48,7 +49,7 @@ public abstract class DataSource { public void test() throws Exception { - store.test(); + store.validate(); } public void validate() throws Exception { @@ -56,7 +57,7 @@ public abstract class DataSource { throw new IllegalStateException("Store cannot be null"); } - store.validate(); + store.checkComplete(); } public WriteMode[] getAvailableWriteModes() { @@ -79,38 +80,12 @@ public abstract class DataSource { @SneakyThrows @SuppressWarnings("unchecked") public > T copy() { - var mapper = JacksonHelper.newMapper(); + var mapper = JacksonMapper.newMapper(); TokenBuffer tb = new TokenBuffer(mapper, false); mapper.writeValue(tb, this); return (T) mapper.readValue(tb.asParser(), getClass()); } - @SneakyThrows - public final String toString() { - var tree = JacksonHelper.newMapper().valueToTree(this); - return tree.toPrettyString(); - } - - @Override - public final boolean equals(Object o) { - if (this == o) { - return true; - } - if (getClass() != o.getClass()) { - return false; - } - - var tree = JacksonHelper.newMapper().valueToTree(this); - var otherTree = JacksonHelper.newMapper().valueToTree(o); - return tree.equals(otherTree); - } - - @Override - public final int hashCode() { - var tree = JacksonHelper.newMapper().valueToTree(this); - return tree.hashCode(); - } - public DataSource withStore(DS store) { var c = copy(); c.store = store; diff --git a/core/src/main/java/io/xpipe/core/source/TableDataSource.java b/core/src/main/java/io/xpipe/core/source/TableDataSource.java index 8832402ea..3c9d09e03 100644 --- a/core/src/main/java/io/xpipe/core/source/TableDataSource.java +++ b/core/src/main/java/io/xpipe/core/source/TableDataSource.java @@ -21,6 +21,12 @@ public abstract class TableDataSource extends DataSource readErrOnly() throws Exception { @@ -34,7 +34,7 @@ public abstract class ProcessControl { t.start(); var ec = waitFor(); - return ec != 0 ? Optional.of(read.get()) : Optional.empty(); + return ec != 0 ? Optional.of(read.get().trim()) : Optional.empty(); } public Thread discardOut() { diff --git a/core/src/main/java/io/xpipe/core/store/ShellStore.java b/core/src/main/java/io/xpipe/core/store/ShellStore.java index ef9e571d2..cc593daac 100644 --- a/core/src/main/java/io/xpipe/core/store/ShellStore.java +++ b/core/src/main/java/io/xpipe/core/store/ShellStore.java @@ -2,28 +2,23 @@ package io.xpipe.core.store; import io.xpipe.core.util.SecretValue; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; -public abstract class ShellStore implements DataStore { +public interface ShellStore extends DataStore { - public Integer getTimeout() { + public default Integer getTimeout() { return null; } - public List getInput() { + public default List getInput() { return List.of(); } - public boolean isLocal() { - return false; - } - - public String executeAndRead(List cmd, Integer timeout) throws Exception { + public default String executeAndRead(List cmd, Integer timeout) throws Exception { var pc = prepareCommand(List.of(), cmd, getEffectiveTimeOut(timeout)); pc.start(); pc.discardErr(); @@ -31,7 +26,8 @@ public abstract class ShellStore implements DataStore { return string; } - public String executeAndCheckOut(List in, List cmd, Integer timeout) throws ProcessOutputException, Exception { + public default String executeAndCheckOut(List in, List cmd, Integer timeout) + throws ProcessOutputException, Exception { var pc = prepareCommand(in, cmd, getEffectiveTimeOut(timeout)); pc.start(); @@ -47,15 +43,10 @@ public abstract class ShellStore implements DataStore { errorThread.setDaemon(true); errorThread.start(); - var read = new ByteArrayOutputStream(); + AtomicReference read = new AtomicReference<>(); var t = new Thread(() -> { try { - final byte[] buf = new byte[1]; - - int length; - while ((length = pc.getStdout().read(buf)) > 0) { - read.write(buf, 0, length); - } + read.set(new String(pc.getStdout().readAllBytes(), pc.getCharset())); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -64,19 +55,18 @@ public abstract class ShellStore implements DataStore { t.start(); var ec = pc.waitFor(); - var readOut = read.toString(pc.getCharset()); if (ec == -1) { throw new ProcessOutputException("Command timed out"); } - if (ec == 0) { - return readOut; + if (ec == 0 && !(read.get().isEmpty() && !readError.get().isEmpty())) { + return read.get().trim(); } else { - throw new ProcessOutputException("Command returned with " + ec + ": " + readError.get()); + throw new ProcessOutputException("Command returned with " + ec + ": " + readError.get().trim()); } } - public Optional executeAndCheckErr(List in, List cmd) throws Exception { + public default Optional executeAndCheckErr(List in, List cmd) throws Exception { var pc = prepareCommand(in, cmd, getTimeout()); pc.start(); var outT = pc.discardOut(); @@ -99,7 +89,7 @@ public abstract class ShellStore implements DataStore { return ec != 0 ? Optional.of(read.get()) : Optional.empty(); } - public Integer getEffectiveTimeOut(Integer timeout) { + public default Integer getEffectiveTimeOut(Integer timeout) { if (this.getTimeout() == null) { return timeout; } @@ -109,17 +99,19 @@ public abstract class ShellStore implements DataStore { return Math.min(getTimeout(), timeout); } - public ProcessControl prepareCommand(List cmd, Integer timeout) throws Exception { + public default ProcessControl prepareCommand(List cmd, Integer timeout) throws Exception { return prepareCommand(List.of(), cmd, timeout); } - public abstract ProcessControl prepareCommand(List input, List cmd, Integer timeout) throws Exception; + public abstract ProcessControl prepareCommand(List input, List cmd, Integer timeout) + throws Exception; - public ProcessControl preparePrivilegedCommand(List cmd, Integer timeout) throws Exception { + public default ProcessControl preparePrivilegedCommand(List cmd, Integer timeout) throws Exception { return preparePrivilegedCommand(List.of(), cmd, timeout); } - public ProcessControl preparePrivilegedCommand(List input, List cmd, Integer timeout) throws Exception { + public default ProcessControl preparePrivilegedCommand(List input, List cmd, Integer timeout) + throws Exception { throw new UnsupportedOperationException(); } } diff --git a/core/src/main/java/io/xpipe/core/store/ShellTypes.java b/core/src/main/java/io/xpipe/core/store/ShellTypes.java index 6e7b156ec..b32f98f6c 100644 --- a/core/src/main/java/io/xpipe/core/store/ShellTypes.java +++ b/core/src/main/java/io/xpipe/core/store/ShellTypes.java @@ -77,8 +77,10 @@ public class ShellTypes { var l = new ArrayList<>(cmd); l.add(0, "cmd.exe"); l.add(1, "/c"); - l.add(2, "@chcp 65001>nul"); - l.add(3, "&&"); + l.add(2, "@chcp 65001"); + l.add(3, ">"); + l.add(4, "nul"); + l.add(5, "&&"); return l; } diff --git a/core/src/main/java/io/xpipe/core/store/StandardShellStore.java b/core/src/main/java/io/xpipe/core/store/StandardShellStore.java index be1e7617d..10da2e3de 100644 --- a/core/src/main/java/io/xpipe/core/store/StandardShellStore.java +++ b/core/src/main/java/io/xpipe/core/store/StandardShellStore.java @@ -9,7 +9,11 @@ import java.io.OutputStream; import java.nio.charset.Charset; import java.util.List; -public abstract class StandardShellStore extends ShellStore implements MachineFileStore { +public interface StandardShellStore extends MachineFileStore, ShellStore { + + public default boolean isLocal() { + return false; + } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") public static interface ShellType { @@ -38,19 +42,19 @@ public abstract class StandardShellStore extends ShellStore implements MachineFi } - public NewLine getNewLine() throws Exception { + public default NewLine getNewLine() throws Exception { return determineType().getNewLine(); } public abstract ShellType determineType() throws Exception; - public final String querySystemName() throws Exception { + public default String querySystemName() throws Exception { var result = executeAndCheckOut(List.of(), determineType().getOperatingSystemNameCommand(), getTimeout()); return result.strip(); } @Override - public InputStream openInput(String file) throws Exception { + public default InputStream openInput(String file) throws Exception { var type = determineType(); var cmd = type.createFileReadCommand(file); var p = prepareCommand(List.of(), cmd, null); @@ -59,7 +63,7 @@ public abstract class StandardShellStore extends ShellStore implements MachineFi } @Override - public OutputStream openOutput(String file) throws Exception { + public default OutputStream openOutput(String file) throws Exception { return null; // var type = determineType(); // var cmd = type.createFileWriteCommand(file); @@ -69,7 +73,8 @@ public abstract class StandardShellStore extends ShellStore implements MachineFi } @Override - public boolean exists(String file) throws Exception { + + public default boolean exists(String file) throws Exception { var type = determineType(); var cmd = type.createFileExistsCommand(file); var p = prepareCommand(List.of(), cmd, null); @@ -78,7 +83,7 @@ public abstract class StandardShellStore extends ShellStore implements MachineFi } @Override - public void mkdirs(String file) throws Exception { + public default void mkdirs(String file) throws Exception { } } diff --git a/core/src/main/java/io/xpipe/core/store/StdinDataStore.java b/core/src/main/java/io/xpipe/core/store/StdinDataStore.java index dde362ea2..947d932f9 100644 --- a/core/src/main/java/io/xpipe/core/store/StdinDataStore.java +++ b/core/src/main/java/io/xpipe/core/store/StdinDataStore.java @@ -1,17 +1,16 @@ package io.xpipe.core.store; import com.fasterxml.jackson.annotation.JsonTypeName; -import lombok.EqualsAndHashCode; -import lombok.Value; +import io.xpipe.core.util.JacksonizedValue; +import lombok.experimental.SuperBuilder; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -@EqualsAndHashCode -@Value @JsonTypeName("stdin") -public class StdinDataStore implements StreamDataStore { +@SuperBuilder +public class StdinDataStore extends JacksonizedValue implements StreamDataStore { @Override public boolean isContentExclusivelyAccessible() { diff --git a/core/src/main/java/io/xpipe/core/store/StdoutDataStore.java b/core/src/main/java/io/xpipe/core/store/StdoutDataStore.java index 3d9752c52..1ab831baf 100644 --- a/core/src/main/java/io/xpipe/core/store/StdoutDataStore.java +++ b/core/src/main/java/io/xpipe/core/store/StdoutDataStore.java @@ -1,16 +1,15 @@ package io.xpipe.core.store; import com.fasterxml.jackson.annotation.JsonTypeName; -import lombok.EqualsAndHashCode; -import lombok.Value; +import io.xpipe.core.util.JacksonizedValue; +import lombok.experimental.SuperBuilder; import java.io.IOException; import java.io.OutputStream; -@EqualsAndHashCode -@Value @JsonTypeName("stdout") -public class StdoutDataStore implements StreamDataStore { +@SuperBuilder +public class StdoutDataStore extends JacksonizedValue implements StreamDataStore { @Override public boolean canOpen() throws Exception { diff --git a/core/src/main/java/io/xpipe/core/util/JacksonHelper.java b/core/src/main/java/io/xpipe/core/util/JacksonMapper.java similarity index 70% rename from core/src/main/java/io/xpipe/core/util/JacksonHelper.java rename to core/src/main/java/io/xpipe/core/util/JacksonMapper.java index 0f26f8e41..2be58d1df 100644 --- a/core/src/main/java/io/xpipe/core/util/JacksonHelper.java +++ b/core/src/main/java/io/xpipe/core/util/JacksonMapper.java @@ -10,35 +10,40 @@ import java.util.ArrayList; import java.util.List; import java.util.ServiceLoader; -public class JacksonHelper { +public class JacksonMapper { private static final ObjectMapper BASE = new ObjectMapper(); private static ObjectMapper INSTANCE = new ObjectMapper(); + private static final ObjectMapper DEFAULT; private static boolean init = false; - private static List MODULES; - - public static synchronized void initClassBased() { - initModularized(null); - } - - public static synchronized void initModularized(ModuleLayer layer) { - MODULES = findModules(layer); + static { ObjectMapper objectMapper = BASE; objectMapper.enable(SerializationFeature.INDENT_OUTPUT); objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); objectMapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker() - .withFieldVisibility(JsonAutoDetect.Visibility.ANY) - .withGetterVisibility(JsonAutoDetect.Visibility.NONE) - .withSetterVisibility(JsonAutoDetect.Visibility.NONE) - .withCreatorVisibility(JsonAutoDetect.Visibility.NONE) - .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withSetterVisibility(JsonAutoDetect.Visibility.NONE) + .withCreatorVisibility(JsonAutoDetect.Visibility.NONE) + .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); + + var modules = findModules(ModuleLayer.boot()); + objectMapper.registerModules(modules); INSTANCE = BASE.copy(); - INSTANCE.registerModules(MODULES); + DEFAULT = BASE.copy(); + } + public static synchronized void initClassBased() { + initModularized(null); + } + + public static synchronized void initModularized(ModuleLayer layer) { + List MODULES = findModules(layer); + INSTANCE.registerModules(MODULES); init = true; } @@ -56,8 +61,8 @@ public class JacksonHelper { * Constructs a new ObjectMapper that is able to map all required X-Pipe classes and also possible extensions. */ public static ObjectMapper newMapper() { - if (!JacksonHelper.isInit()) { - JacksonHelper.initModularized(ModuleLayer.boot()); + if (!JacksonMapper.isInit()) { + return DEFAULT; } return INSTANCE.copy(); } diff --git a/core/src/main/java/io/xpipe/core/util/JacksonizedValue.java b/core/src/main/java/io/xpipe/core/util/JacksonizedValue.java new file mode 100644 index 000000000..b4dbb3a66 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/util/JacksonizedValue.java @@ -0,0 +1,38 @@ +package io.xpipe.core.util; + +import lombok.SneakyThrows; +import lombok.experimental.SuperBuilder; + +@SuperBuilder +public class JacksonizedValue { + + public JacksonizedValue() { + } + + @SneakyThrows + public final String toString() { + var tree = JacksonMapper.newMapper().valueToTree(this); + return tree.toPrettyString(); + } + + @Override + public final boolean equals(Object o) { + if (this == o) { + return true; + } + if (o != null && getClass() != o.getClass()) { + return false; + } + + var tree = JacksonMapper.newMapper().valueToTree(this); + var otherTree = JacksonMapper.newMapper().valueToTree(o); + return tree.equals(otherTree); + } + + @Override + public final int hashCode() { + var tree = JacksonMapper.newMapper().valueToTree(this); + return tree.hashCode(); + } + +} diff --git a/core/src/main/java/io/xpipe/core/util/SecretValue.java b/core/src/main/java/io/xpipe/core/util/SecretValue.java index 193a7530b..de5c822a9 100644 --- a/core/src/main/java/io/xpipe/core/util/SecretValue.java +++ b/core/src/main/java/io/xpipe/core/util/SecretValue.java @@ -36,7 +36,10 @@ public class SecretValue { if (value.length() < 2) { return value; } - - return new String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8); + try { + return new String(Base64.getDecoder().decode(value), StandardCharsets.UTF_8); + } catch (Exception exception) { + return ""; + } } } diff --git a/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java b/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java index 600131ae8..54ab73500 100644 --- a/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java +++ b/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java @@ -61,10 +61,17 @@ public interface DataSourceProvider> { return i18n("displayDescription"); } - default String getDisplayIconFileName() { - return getId() + ":icon.png"; + default String getModuleName() { + var n = getClass().getPackageName(); + var i = n.lastIndexOf('.'); + return i != -1 ? n.substring(i + 1) : n; } + default String getDisplayIconFileName() { + return getModuleName() + ":" + getId() + "_icon.png"; + } + + default String getSourceDescription(T source) { return getDisplayName(); } diff --git a/extension/src/main/java/io/xpipe/extension/DataStoreProvider.java b/extension/src/main/java/io/xpipe/extension/DataStoreProvider.java index e9ccc7438..0c664a43e 100644 --- a/extension/src/main/java/io/xpipe/extension/DataStoreProvider.java +++ b/extension/src/main/java/io/xpipe/extension/DataStoreProvider.java @@ -2,6 +2,7 @@ package io.xpipe.extension; import io.xpipe.core.dialog.Dialog; import io.xpipe.core.store.*; +import io.xpipe.core.util.JacksonizedValue; import javafx.beans.property.Property; import java.util.List; @@ -14,6 +15,16 @@ public interface DataStoreProvider { DATABASE; } + default void validate() throws Exception { + getCategory(); + for (Class storeClass : getStoreClasses()) { + if (!JacksonizedValue.class.isAssignableFrom(storeClass)) { + throw new ExtensionException(String.format("Store class %s is not a Jacksonized value", storeClass.getSimpleName())); + } + } + } + + default Category getCategory() { var c = getStoreClasses().get(0); if (StreamDataStore.class.isAssignableFrom(c)) { diff --git a/extension/src/main/java/io/xpipe/extension/DataStoreProviders.java b/extension/src/main/java/io/xpipe/extension/DataStoreProviders.java index 9d1d8f152..b6fab5871 100644 --- a/extension/src/main/java/io/xpipe/extension/DataStoreProviders.java +++ b/extension/src/main/java/io/xpipe/extension/DataStoreProviders.java @@ -19,9 +19,12 @@ public class DataStoreProviders { .collect(Collectors.toList()); ALL.removeIf(p -> { try { - return !p.init(); + p.init(); + p.validate(); + return false; } catch (Exception e) { - ErrorEvent.fromThrowable(e).handle(); + ErrorEvent.fromThrowable(e) + .handle(); return true; } }); @@ -56,17 +59,17 @@ public class DataStoreProviders { @SuppressWarnings("unchecked") public static T byStore(DataStore store) { - return (T) byStoreClass(store.getClass()); + return (T) byStoreClass(store.getClass()).orElseThrow(); } @SuppressWarnings("unchecked") - public static T byStoreClass(Class c) { + public static Optional byStoreClass(Class c) { if (ALL == null) { throw new IllegalStateException("Not initialized"); } - return (T) ALL.stream().filter(d -> d.getStoreClasses().contains(c)).findAny().orElseThrow(); + return (Optional) ALL.stream().filter(d -> d.getStoreClasses().contains(c)).findAny(); } public static List getAll() { diff --git a/extension/src/main/java/io/xpipe/extension/comp/FancyTooltipAugment.java b/extension/src/main/java/io/xpipe/extension/comp/FancyTooltipAugment.java index 7305e4d2e..e85ed9744 100644 --- a/extension/src/main/java/io/xpipe/extension/comp/FancyTooltipAugment.java +++ b/extension/src/main/java/io/xpipe/extension/comp/FancyTooltipAugment.java @@ -80,7 +80,10 @@ public class FancyTooltipAugment> implements Augment< public void hide() { Window owner = getOwnerWindow(); if (owner == null || owner.isFocused()) { - super.hide(); + try { + super.hide(); + } catch (Exception e) { + } } } diff --git a/extension/src/main/java/io/xpipe/extension/comp/FilterComp.java b/extension/src/main/java/io/xpipe/extension/comp/FilterComp.java index 67f9b0a4d..4c57a26f2 100644 --- a/extension/src/main/java/io/xpipe/extension/comp/FilterComp.java +++ b/extension/src/main/java/io/xpipe/extension/comp/FilterComp.java @@ -47,8 +47,6 @@ public class FilterComp extends Comp { var stack = new StackPane(bgLabel, filter); stack.getStyleClass().add("filter-comp"); - bgLabel.prefHeightProperty().bind(stack.heightProperty()); - filter.prefHeightProperty().bind(stack.heightProperty()); return Structure.builder().inactiveIcon(fi).inactiveText(bgLabel).text(filter).pane(stack).build(); } diff --git a/extension/src/main/java/io/xpipe/extension/comp/SvgComp.java b/extension/src/main/java/io/xpipe/extension/comp/SvgComp.java new file mode 100644 index 000000000..2c6c59f26 --- /dev/null +++ b/extension/src/main/java/io/xpipe/extension/comp/SvgComp.java @@ -0,0 +1,124 @@ +package io.xpipe.extension.comp; + +import io.xpipe.fxcomps.CompStructure; +import io.xpipe.fxcomps.util.PlatformThread; +import io.xpipe.fxcomps.util.SimpleChangeListener; +import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.ListChangeListener; +import javafx.css.Size; +import javafx.css.SizeUnits; +import javafx.scene.Node; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.scene.web.WebView; +import lombok.Builder; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.Value; + +import java.util.Set; +import java.util.regex.Pattern; + +@Getter +public class SvgComp { + + private static Size parseSize(String string) { + for (SizeUnits unit : SizeUnits.values()) { + if (string.endsWith(unit.toString())) { + return new Size( + Double.parseDouble(string.substring( + 0, string.length() - unit.toString().length())), + unit); + } + } + return new Size(Double.parseDouble(string), SizeUnits.PX); + } + + @SneakyThrows + public static SvgComp create(ObservableValue content) { + var widthProperty = new SimpleIntegerProperty(); + var heightProperty = new SimpleIntegerProperty(); + SimpleChangeListener.apply(content, val -> { + var regularExpression = Pattern.compile(" { + StackPane pane; + WebView webView; + + @Override + public StackPane get() { + return pane; + } + } + + private final ObservableValue width; + private final ObservableValue height; + private final ObservableValue svgContent; + + public SvgComp(ObservableValue width, ObservableValue height, ObservableValue svgContent) { + this.width = PlatformThread.sync(width); + this.height = PlatformThread.sync(height); + this.svgContent = PlatformThread.sync(svgContent); + } + + private String getHtml(String content) { + return "" + content + ""; + } + + private WebView createWebView() { + var wv = new WebView(); + wv.setPageFill(Color.TRANSPARENT); + wv.setDisable(true); + + wv.getEngine().loadContent(getHtml(svgContent.getValue())); + svgContent.addListener((c, o, n) -> { + wv.getEngine().loadContent(getHtml(n)); + }); + + // Hide scrollbars that popup on every content change. Bug in WebView? + wv.getChildrenUnmodifiable().addListener((ListChangeListener) change -> { + Set scrolls = wv.lookupAll(".scroll-bar"); + for (Node scroll : scrolls) { + scroll.setVisible(false); + } + }); + + // As the aspect ratio of the WebView is kept constant, we can compute the zoom only using the width + wv.zoomProperty() + .bind(Bindings.createDoubleBinding( + () -> { + return wv.getWidth() / width.getValue().doubleValue(); + }, + wv.widthProperty(), + width)); + + wv.maxWidthProperty().bind(wv.prefWidthProperty()); + wv.maxHeightProperty().bind(wv.prefHeightProperty()); + return wv; + } + + + public WebView createWebview() { + var wv = createWebView(); +wv.getStyleClass().add("svg-comp"); + return wv; + } +} diff --git a/extension/src/main/java/io/xpipe/extension/comp/WriteModeChoiceComp.java b/extension/src/main/java/io/xpipe/extension/comp/WriteModeChoiceComp.java index 9cc82e597..b45a5cfaa 100644 --- a/extension/src/main/java/io/xpipe/extension/comp/WriteModeChoiceComp.java +++ b/extension/src/main/java/io/xpipe/extension/comp/WriteModeChoiceComp.java @@ -14,6 +14,7 @@ import lombok.EqualsAndHashCode; import lombok.Value; import net.synedra.validatorfx.Check; +import java.util.Arrays; import java.util.LinkedHashMap; @Value @@ -28,19 +29,41 @@ public class WriteModeChoiceComp extends SimpleComp implements Validatable { public WriteModeChoiceComp(Property selected, WriteMode[] available) { this.selected = selected; this.available = available; + if (available.length == 1) { + selected.setValue(available[0]); + } check = Validators.nonNull(validator, I18n.observable("mode"), selected); } @Override protected Region createSimple() { + var a = Arrays.asList(available); var map = new LinkedHashMap>(); - map.put(WriteMode.REPLACE, I18n.observable("replace")); - map.put(WriteMode.APPEND, I18n.observable("append")); - map.put(WriteMode.PREPEND, I18n.observable("prepend")); + var replaceIndex = -1; + if (a.contains(WriteMode.REPLACE)) { + map.put(WriteMode.REPLACE, I18n.observable("replace")); + replaceIndex = 0; + } + + var appendIndex = -1; + if (a.contains(WriteMode.APPEND)) { + map.put(WriteMode.APPEND, I18n.observable("append")); + appendIndex = replaceIndex + 1; + } + + var prependIndex = -1; + if (a.contains(WriteMode.PREPEND)) { + map.put(WriteMode.PREPEND, I18n.observable("prepend")); + prependIndex = Math.max(replaceIndex, appendIndex) + 1; + } + + int finalReplaceIndex = replaceIndex; + int finalAppendIndex = appendIndex; + int finalPrependIndex = prependIndex; return new ToggleGroupComp<>(selected, map).apply(struc -> { - new FancyTooltipAugment<>("extension.replaceDescription").augment(struc.get().getChildren().get(0)); - new FancyTooltipAugment<>("extension.appendDescription").augment(struc.get().getChildren().get(1)); - new FancyTooltipAugment<>("extension.prependDescription").augment(struc.get().getChildren().get(2)); + if (finalReplaceIndex != -1) new FancyTooltipAugment<>("extension.replaceDescription").augment(struc.get().getChildren().get(0)); + if (finalAppendIndex != -1) new FancyTooltipAugment<>("extension.appendDescription").augment(struc.get().getChildren().get(finalAppendIndex)); + if (finalPrependIndex != -1) new FancyTooltipAugment<>("extension.prependDescription").augment(struc.get().getChildren().get(finalPrependIndex)); }).apply(struc -> check.decorates(struc.get())).createRegion(); } } diff --git a/extension/src/main/java/io/xpipe/extension/prefs/PrefsChoiceValue.java b/extension/src/main/java/io/xpipe/extension/prefs/PrefsChoiceValue.java index a52428210..ac2787daa 100644 --- a/extension/src/main/java/io/xpipe/extension/prefs/PrefsChoiceValue.java +++ b/extension/src/main/java/io/xpipe/extension/prefs/PrefsChoiceValue.java @@ -47,9 +47,5 @@ public interface PrefsChoiceValue extends Translatable { return I18n.get(getId()); } - default String toDefaultString() { - return I18n.get(getId()); - } - String getId(); } diff --git a/extension/src/main/java/io/xpipe/extension/util/CustomComboBoxBuilder.java b/extension/src/main/java/io/xpipe/extension/util/CustomComboBoxBuilder.java index f759b6798..b043dc9d9 100644 --- a/extension/src/main/java/io/xpipe/extension/util/CustomComboBoxBuilder.java +++ b/extension/src/main/java/io/xpipe/extension/util/CustomComboBoxBuilder.java @@ -101,8 +101,6 @@ public class CustomComboBoxBuilder { cb.valueProperty().addListener((c, o, n) -> { if (nodeMap.containsKey(n)) { if (veto != null && !veto.test(nodeMap.get(n))) { - cb.setValue(o); - ; return; } selected.setValue(nodeMap.get(n)); diff --git a/extension/src/main/java/io/xpipe/extension/util/PrettyListView.java b/extension/src/main/java/io/xpipe/extension/util/PrettyListView.java index 7c62d1a68..629b9e08e 100644 --- a/extension/src/main/java/io/xpipe/extension/util/PrettyListView.java +++ b/extension/src/main/java/io/xpipe/extension/util/PrettyListView.java @@ -1,15 +1,85 @@ package io.xpipe.extension.util; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.scene.Node; import javafx.scene.control.ListView; +import javafx.scene.control.MultipleSelectionModel; import javafx.scene.control.ScrollBar; import java.util.Set; public class PrettyListView extends ListView { + public static class NoSelectionModel extends MultipleSelectionModel { + + @Override + public ObservableList getSelectedIndices() { + return FXCollections.emptyObservableList(); + } + + @Override + public ObservableList getSelectedItems() { + return FXCollections.emptyObservableList(); + } + + @Override + public void selectIndices(int index, int... indices) { + } + + @Override + public void selectAll() { + } + + @Override + public void selectFirst() { + } + + @Override + public void selectLast() { + } + + @Override + public void clearAndSelect(int index) { + } + + @Override + public void select(int index) { + } + + @Override + public void select(T obj) { + } + + @Override + public void clearSelection(int index) { + } + + @Override + public void clearSelection() { + } + + @Override + public boolean isSelected(int index) { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public void selectPrevious() { + } + + @Override + public void selectNext() { + } + } + private final ScrollBar vBar = new ScrollBar(); private final ScrollBar hBar = new ScrollBar(); @@ -34,6 +104,10 @@ public class PrettyListView extends ListView { hBar.visibleProperty().setValue(false); } + public void disableSelection() { + setSelectionModel(new NoSelectionModel<>()); + } + private void bindScrollBars() { final Set nodes = lookupAll("VirtualScrollBar"); for (Node node : nodes) { diff --git a/extension/src/main/java/io/xpipe/extension/util/Validators.java b/extension/src/main/java/io/xpipe/extension/util/Validators.java index 382869f06..20070d966 100644 --- a/extension/src/main/java/io/xpipe/extension/util/Validators.java +++ b/extension/src/main/java/io/xpipe/extension/util/Validators.java @@ -1,5 +1,7 @@ package io.xpipe.extension.util; +import io.xpipe.core.store.DataStore; +import io.xpipe.core.store.LocalStore; import io.xpipe.core.store.ShellStore; import io.xpipe.extension.I18n; import javafx.beans.value.ObservableValue; @@ -11,7 +13,7 @@ public class Validators { public static Check nonNull(Validator v, ObservableValue name, ObservableValue s) { return v.createCheck().dependsOn("val", s).withMethod(c -> { - if (c.get("val") == null ) { + if (c.get("val") == null) { c.error(I18n.get("extension.mustNotBeEmpty", name.getValue())); } }); @@ -23,6 +25,12 @@ public class Validators { } } + public static void namedStoreExists(DataStore store, String name) { + if (!XPipeDaemon.getInstance().getNamedStores().contains(store) && !(store instanceof LocalStore)) { + throw new IllegalArgumentException(I18n.get("extension.missingStore", name)); + } + } + public static void hostFeature(ShellStore host, Predicate predicate, String name) { if (!predicate.test(host)) { throw new IllegalArgumentException(I18n.get("extension.hostFeatureUnsupported", name)); diff --git a/extension/src/main/java/io/xpipe/extension/util/XPipeDaemon.java b/extension/src/main/java/io/xpipe/extension/util/XPipeDaemon.java index 20693d83a..54e94aedd 100644 --- a/extension/src/main/java/io/xpipe/extension/util/XPipeDaemon.java +++ b/extension/src/main/java/io/xpipe/extension/util/XPipeDaemon.java @@ -25,7 +25,9 @@ public interface XPipeDaemon { public Image image(String file); - & Validatable> T streamStoreChooser(Property storeProperty, Property> provider); + & Validatable> T streamStoreChooser(Property storeProperty, Property> provider, + boolean showAnonymous, + boolean showSaved); & Validatable> T namedStoreChooser( ObservableValue> filter, Property selected, DataStoreProvider.Category category diff --git a/extension/src/main/java/module-info.java b/extension/src/main/java/module-info.java index 091c4d44e..40c1f3311 100644 --- a/extension/src/main/java/module-info.java +++ b/extension/src/main/java/module-info.java @@ -18,6 +18,7 @@ open module io.xpipe.extension { requires static javafx.base; requires static javafx.graphics; requires static javafx.controls; + requires static javafx.web; requires static io.xpipe.fxcomps; requires static lombok; requires static org.controlsfx.controls; diff --git a/extension/src/main/resources/io/xpipe/extension/resources/lang/translations_en.properties b/extension/src/main/resources/io/xpipe/extension/resources/lang/translations_en.properties index 3605c4fd7..e289359fd 100644 --- a/extension/src/main/resources/io/xpipe/extension/resources/lang/translations_en.properties +++ b/extension/src/main/resources/io/xpipe/extension/resources/lang/translations_en.properties @@ -7,6 +7,7 @@ nullPointer=Null Pointer: $MSG$ mustNotBeEmpty=$NAME$ must not be empty null=$VALUE$ must be not null hostFeatureUnsupported=Host does not support the feature $FEATURE$ +missingStore=$NAME$ does not exist namedHostFeatureUnsupported=$HOST$ does not support this feature namedHostNotActive=$HOST$ is not active noInformationAvailable=No information available