From a0c008ab3b70d0739971f56a49d0da2f9d8d6849 Mon Sep 17 00:00:00 2001 From: Christopher Schnick Date: Sun, 30 Oct 2022 04:51:01 +0100 Subject: [PATCH] Implement support for custom write modes --- .../io/xpipe/core/data/type/TupleType.java | 8 ++++ .../java/io/xpipe/core/impl/BinarySource.java | 3 +- .../core/impl/PreservingWriteConnection.java | 3 +- .../java/io/xpipe/core/impl/TextSource.java | 13 +----- .../java/io/xpipe/core/impl/XpbsSource.java | 3 +- .../java/io/xpipe/core/impl/XpbtSource.java | 3 +- .../core/source/CollectionDataSource.java | 10 ++-- .../java/io/xpipe/core/source/DataSource.java | 17 ++----- .../io/xpipe/core/source/RawDataSource.java | 10 ++-- .../core/source/StructureDataSource.java | 10 ++-- .../io/xpipe/core/source/TableDataSource.java | 36 ++++++--------- .../io/xpipe/core/source/TableMapping.java | 22 +++++---- .../io/xpipe/core/source/TextDataSource.java | 32 ++++--------- .../java/io/xpipe/core/source/WriteMode.java | 45 ++++++++++++------ .../io/xpipe/core/util/JacksonizedValue.java | 2 + core/src/main/java/module-info.java | 3 ++ .../extension/XPipeServiceProviders.java | 36 +++++++++++++++ .../xpipe/extension/comp/ToggleGroupComp.java | 11 +++-- .../extension/comp/WriteModeChoiceComp.java | 46 +++++-------------- .../xpipe/extension/util/ExtensionTest.java | 6 ++- 20 files changed, 176 insertions(+), 143 deletions(-) create mode 100644 extension/src/main/java/io/xpipe/extension/XPipeServiceProviders.java diff --git a/core/src/main/java/io/xpipe/core/data/type/TupleType.java b/core/src/main/java/io/xpipe/core/data/type/TupleType.java index 3dd9d00fb..a73b48cf3 100644 --- a/core/src/main/java/io/xpipe/core/data/type/TupleType.java +++ b/core/src/main/java/io/xpipe/core/data/type/TupleType.java @@ -60,6 +60,14 @@ public class TupleType extends DataType { return names.stream().allMatch(Objects::nonNull); } + public TupleType sub(List subNames) { + if (!hasAllNames()) { + throw new UnsupportedOperationException(); + } + + return new TupleType(subNames, subNames.stream().map(s -> types.get(getNames().indexOf(s))).toList()); + } + @Override public String getName() { return "tuple"; diff --git a/core/src/main/java/io/xpipe/core/impl/BinarySource.java b/core/src/main/java/io/xpipe/core/impl/BinarySource.java index f0d83de1b..92b28e975 100644 --- a/core/src/main/java/io/xpipe/core/impl/BinarySource.java +++ b/core/src/main/java/io/xpipe/core/impl/BinarySource.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import io.xpipe.core.source.RawDataSource; import io.xpipe.core.source.RawReadConnection; import io.xpipe.core.source.RawWriteConnection; +import io.xpipe.core.source.WriteMode; import io.xpipe.core.store.StreamDataStore; import lombok.experimental.SuperBuilder; @@ -15,7 +16,7 @@ import java.io.OutputStream; public class BinarySource extends RawDataSource { @Override - protected RawWriteConnection newWriteConnection() { + protected RawWriteConnection newWriteConnection(WriteMode mode) { return new RawWriteConnection() { private OutputStream out; diff --git a/core/src/main/java/io/xpipe/core/impl/PreservingWriteConnection.java b/core/src/main/java/io/xpipe/core/impl/PreservingWriteConnection.java index 8b101f7f9..4a7a30f0d 100644 --- a/core/src/main/java/io/xpipe/core/impl/PreservingWriteConnection.java +++ b/core/src/main/java/io/xpipe/core/impl/PreservingWriteConnection.java @@ -3,6 +3,7 @@ package io.xpipe.core.impl; import io.xpipe.core.source.DataSource; import io.xpipe.core.source.DataSourceConnection; import io.xpipe.core.source.DataSourceType; +import io.xpipe.core.source.WriteMode; import io.xpipe.core.store.FileStore; import java.nio.file.Files; @@ -28,7 +29,7 @@ public class PreservingWriteConnection implements DataSourceConnection { var nativeSource = DataSource.createInternalDataSource(type, nativeStore); if (source.getStore().canOpen()) { try (var in = source.openReadConnection(); - var out = nativeSource.openWriteConnection()) { + var out = nativeSource.openWriteConnection(WriteMode.REPLACE)) { in.forward(out); } ; diff --git a/core/src/main/java/io/xpipe/core/impl/TextSource.java b/core/src/main/java/io/xpipe/core/impl/TextSource.java index b464d7603..e21c8c7ee 100644 --- a/core/src/main/java/io/xpipe/core/impl/TextSource.java +++ b/core/src/main/java/io/xpipe/core/impl/TextSource.java @@ -5,6 +5,7 @@ import io.xpipe.core.charsetter.Charsettable; import io.xpipe.core.charsetter.NewLine; import io.xpipe.core.charsetter.StreamCharset; import io.xpipe.core.source.TextDataSource; +import io.xpipe.core.source.WriteMode; import io.xpipe.core.store.StreamDataStore; import lombok.Getter; import lombok.experimental.SuperBuilder; @@ -20,20 +21,10 @@ public final class TextSource extends TextDataSource implements private final NewLine newLine; @Override - protected io.xpipe.core.source.TextWriteConnection newWriteConnection() { + protected io.xpipe.core.source.TextWriteConnection newWriteConnection(WriteMode mode) { return new TextWriteConnection(this); } - @Override - protected io.xpipe.core.source.TextWriteConnection newPrependingWriteConnection() { - return new PreservingTextWriteConnection(this, newWriteConnection(), false); - } - - @Override - protected io.xpipe.core.source.TextWriteConnection newAppendingWriteConnection() { - return new PreservingTextWriteConnection(this, newWriteConnection(), true); - } - @Override protected io.xpipe.core.source.TextReadConnection newReadConnection() { return new TextReadConnection(this); diff --git a/core/src/main/java/io/xpipe/core/impl/XpbsSource.java b/core/src/main/java/io/xpipe/core/impl/XpbsSource.java index 53bd9f150..94c5bfe12 100644 --- a/core/src/main/java/io/xpipe/core/impl/XpbsSource.java +++ b/core/src/main/java/io/xpipe/core/impl/XpbsSource.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import io.xpipe.core.source.StructureDataSource; import io.xpipe.core.source.StructureReadConnection; import io.xpipe.core.source.StructureWriteConnection; +import io.xpipe.core.source.WriteMode; import io.xpipe.core.store.StreamDataStore; import lombok.experimental.SuperBuilder; import lombok.extern.jackson.Jacksonized; @@ -14,7 +15,7 @@ import lombok.extern.jackson.Jacksonized; public class XpbsSource extends StructureDataSource { @Override - protected StructureWriteConnection newWriteConnection() { + protected StructureWriteConnection newWriteConnection(WriteMode mode) { return new XpbsWriteConnection(this); } diff --git a/core/src/main/java/io/xpipe/core/impl/XpbtSource.java b/core/src/main/java/io/xpipe/core/impl/XpbtSource.java index b615f3db1..9eb4c6f23 100644 --- a/core/src/main/java/io/xpipe/core/impl/XpbtSource.java +++ b/core/src/main/java/io/xpipe/core/impl/XpbtSource.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName; import io.xpipe.core.source.TableDataSource; import io.xpipe.core.source.TableReadConnection; import io.xpipe.core.source.TableWriteConnection; +import io.xpipe.core.source.WriteMode; import io.xpipe.core.store.StreamDataStore; import lombok.experimental.SuperBuilder; import lombok.extern.jackson.Jacksonized; @@ -14,7 +15,7 @@ import lombok.extern.jackson.Jacksonized; public class XpbtSource extends TableDataSource { @Override - protected TableWriteConnection newWriteConnection() { + protected TableWriteConnection newWriteConnection(WriteMode mode) { return new XpbtWriteConnection(this); } diff --git a/core/src/main/java/io/xpipe/core/source/CollectionDataSource.java b/core/src/main/java/io/xpipe/core/source/CollectionDataSource.java index 2eee7aa8a..a8d939ba0 100644 --- a/core/src/main/java/io/xpipe/core/source/CollectionDataSource.java +++ b/core/src/main/java/io/xpipe/core/source/CollectionDataSource.java @@ -36,13 +36,17 @@ public abstract class CollectionDataSource extends DataSou return con; } - public final CollectionWriteConnection openWriteConnection() throws Exception { - var con = newWriteConnection(); + public final CollectionWriteConnection openWriteConnection(WriteMode mode) throws Exception { + var con = newWriteConnection(mode); + if (con == null) { + throw new UnsupportedOperationException(mode.getId()); + } + con.init(); return con; } - protected abstract CollectionWriteConnection newWriteConnection(); + protected abstract CollectionWriteConnection newWriteConnection(WriteMode mode); protected abstract CollectionReadConnection newReadConnection(); } 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 1985794c4..d4c2eb4e2 100644 --- a/core/src/main/java/io/xpipe/core/source/DataSource.java +++ b/core/src/main/java/io/xpipe/core/source/DataSource.java @@ -13,6 +13,7 @@ import io.xpipe.core.util.JacksonizedValue; import lombok.SneakyThrows; import lombok.experimental.SuperBuilder; +import java.util.List; import java.util.Optional; /** @@ -57,12 +58,12 @@ public abstract class DataSource extends JacksonizedValue store.checkComplete(); } - public WriteMode[] getAvailableWriteModes() { + public List getAvailableWriteModes() { if (getFlow() != null && !getFlow().hasOutput()) { - return new WriteMode[0]; + return List.of(); } - return WriteMode.values(); + return List.of(WriteMode.REPLACE, WriteMode.APPEND, WriteMode.PREPEND); } public DataFlow getFlow() { @@ -115,18 +116,10 @@ public abstract class DataSource extends JacksonizedValue throw new UnsupportedOperationException(); } - public DataSourceConnection openWriteConnection() throws Exception { + public DataSourceConnection openWriteConnection(WriteMode mode) throws Exception { throw new UnsupportedOperationException(); } - public DataSourceConnection openAppendingWriteConnection() throws Exception { - throw new UnsupportedOperationException("Appending write is not supported"); - } - - public DataSourceConnection openPrependingWriteConnection() throws Exception { - throw new UnsupportedOperationException("Prepending write is not supported"); - } - public DS getStore() { return store; } diff --git a/core/src/main/java/io/xpipe/core/source/RawDataSource.java b/core/src/main/java/io/xpipe/core/source/RawDataSource.java index 5930d9c93..5dec34a1a 100644 --- a/core/src/main/java/io/xpipe/core/source/RawDataSource.java +++ b/core/src/main/java/io/xpipe/core/source/RawDataSource.java @@ -25,13 +25,17 @@ public abstract class RawDataSource extends DataSource } @Override - public final RawWriteConnection openWriteConnection() throws Exception { - var con = newWriteConnection(); + public final RawWriteConnection openWriteConnection(WriteMode mode) throws Exception { + var con = newWriteConnection(mode); + if (con == null) { + throw new UnsupportedOperationException(mode.getId()); + } + con.init(); return con; } - protected abstract RawWriteConnection newWriteConnection(); + protected abstract RawWriteConnection newWriteConnection(WriteMode mode); protected abstract RawReadConnection newReadConnection(); } diff --git a/core/src/main/java/io/xpipe/core/source/StructureDataSource.java b/core/src/main/java/io/xpipe/core/source/StructureDataSource.java index 460986c19..fbb0e27d4 100644 --- a/core/src/main/java/io/xpipe/core/source/StructureDataSource.java +++ b/core/src/main/java/io/xpipe/core/source/StructureDataSource.java @@ -34,13 +34,17 @@ public abstract class StructureDataSource extends DataSour return con; } - public final StructureWriteConnection openWriteConnection() throws Exception { - var con = newWriteConnection(); + public final StructureWriteConnection openWriteConnection(WriteMode mode) throws Exception { + var con = newWriteConnection(mode); + if (con == null) { + throw new UnsupportedOperationException(mode.getId()); + } + con.init(); return con; } - protected abstract StructureWriteConnection newWriteConnection(); + protected abstract StructureWriteConnection newWriteConnection(WriteMode mode); protected abstract StructureReadConnection newReadConnection(); } 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 aa2b08844..ef1c61b8b 100644 --- a/core/src/main/java/io/xpipe/core/source/TableDataSource.java +++ b/core/src/main/java/io/xpipe/core/source/TableDataSource.java @@ -47,34 +47,26 @@ public abstract class TableDataSource extends DataSource inverseMap(value).isPresent()); } + public boolean isComplete(List outputNames) { + return IntStream.range(0, outputType.getSize()) + .filter(i -> outputNames.contains(outputType.getNames().get(i))) + .allMatch(value -> inverseMap(value).isPresent()); + } + public TableMapping sub(List outputNames) { - var array = new Integer[inputType.getSize()]; - for (int i = 0; i < outputNames.size(); i++) { - var index = inverseMap(outputType.getNames().indexOf(outputNames.get(i))); - if (index.isPresent()) { - array[index.getAsInt()] = i; - } else { - throw new IllegalStateException(); + var array = Arrays.copyOf(columMap, columMap.length); + for (int i = 0; i < inputType.getSize(); i++) { + var mapped = map(i); + if (mapped.isPresent() + && !outputNames.contains(outputType.getNames().get(mapped.getAsInt()))) { + array[i] = null; } } return new TableMapping(inputType, outputType, array); diff --git a/core/src/main/java/io/xpipe/core/source/TextDataSource.java b/core/src/main/java/io/xpipe/core/source/TextDataSource.java index e9258978e..89c1108ac 100644 --- a/core/src/main/java/io/xpipe/core/source/TextDataSource.java +++ b/core/src/main/java/io/xpipe/core/source/TextDataSource.java @@ -37,34 +37,22 @@ public abstract class TextDataSource extends DataSource, DataSourceConnection, Exception> connectionOpener; +public class WriteMode extends JacksonizedValue { - WriteMode(FailableFunction, DataSourceConnection, Exception> connectionOpener) { - this.connectionOpener = connectionOpener; + private static final List ALL = new ArrayList<>(); + + public static void init(ModuleLayer layer) { + if (ALL.size() == 0) { + ALL.addAll(ServiceLoader.load(layer, WriteMode.class).stream() + .map(p -> p.get()) + .toList()); + } } - public DataSourceConnection open(DataSource source) throws Exception { - return connectionOpener.apply(source); + @JsonTypeName("replace") + public static final class Replace extends WriteMode { } - public static interface FailableFunction { - R apply(T input) throws E; + @JsonTypeName("append") + public static final class Append extends WriteMode { + } + + @JsonTypeName("prepend") + public static final class Prepend extends WriteMode { + } + + public static final Replace REPLACE = new Replace(); + public static final Append APPEND = new Append(); + public static final Prepend PREPEND = new Prepend(); + + public final String getId() { + return getClass().getAnnotation(JsonTypeName.class).value(); } } diff --git a/core/src/main/java/io/xpipe/core/util/JacksonizedValue.java b/core/src/main/java/io/xpipe/core/util/JacksonizedValue.java index c56c79b3b..cedf61596 100644 --- a/core/src/main/java/io/xpipe/core/util/JacksonizedValue.java +++ b/core/src/main/java/io/xpipe/core/util/JacksonizedValue.java @@ -1,9 +1,11 @@ package io.xpipe.core.util; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.SneakyThrows; import lombok.experimental.SuperBuilder; @SuperBuilder +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") public class JacksonizedValue { public JacksonizedValue() {} diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index ff2dcc0b2..11dc79e46 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -1,3 +1,4 @@ +import io.xpipe.core.source.WriteMode; import io.xpipe.core.util.CoreJacksonModule; open module io.xpipe.core { @@ -20,7 +21,9 @@ open module io.xpipe.core { requires static lombok; uses com.fasterxml.jackson.databind.Module; + uses io.xpipe.core.source.WriteMode; + provides WriteMode with WriteMode.Replace, WriteMode.Append, WriteMode.Prepend; provides com.fasterxml.jackson.databind.Module with CoreJacksonModule; } diff --git a/extension/src/main/java/io/xpipe/extension/XPipeServiceProviders.java b/extension/src/main/java/io/xpipe/extension/XPipeServiceProviders.java new file mode 100644 index 000000000..2e4ac521c --- /dev/null +++ b/extension/src/main/java/io/xpipe/extension/XPipeServiceProviders.java @@ -0,0 +1,36 @@ +package io.xpipe.extension; + +import com.fasterxml.jackson.databind.jsontype.NamedType; +import io.xpipe.core.util.JacksonMapper; +import io.xpipe.extension.event.TrackEvent; +import io.xpipe.extension.prefs.PrefsProviders; + +public class XPipeServiceProviders { + + public static void load(ModuleLayer layer) { + TrackEvent.info("Loading extension providers ..."); + DataSourceProviders.init(layer); + for (DataSourceProvider p : DataSourceProviders.getAll()) { + TrackEvent.trace("Loaded data source provider " + p.getId()); + JacksonMapper.configure(objectMapper -> { + objectMapper.registerSubtypes(new NamedType(p.getSourceClass())); + }); + } + + DataStoreProviders.init(layer); + for (DataStoreProvider p : DataStoreProviders.getAll()) { + TrackEvent.trace("Loaded data store provider " + p.getId()); + JacksonMapper.configure(objectMapper -> { + for (Class storeClass : p.getStoreClasses()) { + objectMapper.registerSubtypes(new NamedType(storeClass)); + } + }); + } + + DataStoreActionProvider.init(layer); + + SupportedApplicationProviders.loadAll(layer); + PrefsProviders.init(layer); + TrackEvent.info("Finished loading extension providers"); + } +} diff --git a/extension/src/main/java/io/xpipe/extension/comp/ToggleGroupComp.java b/extension/src/main/java/io/xpipe/extension/comp/ToggleGroupComp.java index 5a64a1c2f..a2249186d 100644 --- a/extension/src/main/java/io/xpipe/extension/comp/ToggleGroupComp.java +++ b/extension/src/main/java/io/xpipe/extension/comp/ToggleGroupComp.java @@ -42,11 +42,14 @@ public class ToggleGroupComp extends Comp> { b.setSelected(true); } } - box.getChildren().get(0).getStyleClass().add("first"); - for (int i = 1; i < box.getChildren().size() - 1; i++) { - box.getChildren().get(i).getStyleClass().add("center"); + + if (box.getChildren().size() > 0) { + box.getChildren().get(0).getStyleClass().add("first"); + for (int i = 1; i < box.getChildren().size() - 1; i++) { + box.getChildren().get(i).getStyleClass().add("center"); + } + box.getChildren().get(box.getChildren().size() - 1).getStyleClass().add("last"); } - box.getChildren().get(box.getChildren().size() - 1).getStyleClass().add("last"); group.selectedToggleProperty().addListener((obsVal, oldVal, newVal) -> { if (newVal == null) oldVal.setSelected(true); 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 795ca9ccd..1143b4d0e 100644 --- a/extension/src/main/java/io/xpipe/extension/comp/WriteModeChoiceComp.java +++ b/extension/src/main/java/io/xpipe/extension/comp/WriteModeChoiceComp.java @@ -14,63 +14,41 @@ import lombok.EqualsAndHashCode; import lombok.Value; import net.synedra.validatorfx.Check; -import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; @Value @EqualsAndHashCode(callSuper = true) public class WriteModeChoiceComp extends SimpleComp implements Validatable { Property selected; - WriteMode[] available; + List available; Validator validator = new SimpleValidator(); Check check; - public WriteModeChoiceComp(Property selected, WriteMode[] available) { + public WriteModeChoiceComp(Property selected, List available) { this.selected = selected; this.available = available; - if (available.length == 1) { - selected.setValue(available[0]); + if (available.size() == 1) { + selected.setValue(available.get(0)); } check = Validators.nonNull(validator, I18n.observable("mode"), selected); } @Override protected Region createSimple() { - var a = Arrays.asList(available); + var a = available; var map = new LinkedHashMap>(); - var replaceIndex = -1; - if (a.contains(WriteMode.REPLACE)) { - map.put(WriteMode.REPLACE, I18n.observable("replace")); - replaceIndex = 0; + for (WriteMode writeMode : a) { + map.put(writeMode,I18n.observable(writeMode.getId())); } - 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 -> { - 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)); + for (int i = 0; i < a.size(); i++) { + new FancyTooltipAugment<>(a.get(i).getId() + "Description") + .augment(struc.get().getChildren().get(i)); + } }) .apply(struc -> check.decorates(struc.get())) .createRegion(); diff --git a/extension/src/main/java/io/xpipe/extension/util/ExtensionTest.java b/extension/src/main/java/io/xpipe/extension/util/ExtensionTest.java index 97aa7c044..6934db3bd 100644 --- a/extension/src/main/java/io/xpipe/extension/util/ExtensionTest.java +++ b/extension/src/main/java/io/xpipe/extension/util/ExtensionTest.java @@ -4,7 +4,8 @@ import io.xpipe.api.DataSource; import io.xpipe.api.util.XPipeDaemonController; import io.xpipe.core.store.DataStore; import io.xpipe.core.store.FileStore; -import io.xpipe.extension.DataSourceProviders; +import io.xpipe.core.util.JacksonMapper; +import io.xpipe.extension.XPipeServiceProviders; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -35,7 +36,8 @@ public class ExtensionTest { @BeforeAll public static void setup() throws Exception { - DataSourceProviders.init(ModuleLayer.boot()); + JacksonMapper.initModularized(ModuleLayer.boot()); + XPipeServiceProviders.load(ModuleLayer.boot()); XPipeDaemonController.start(); }