diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/EditExecuteExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/EditExecuteExchange.java index 0f107b4dc..d5ee2867b 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/EditExecuteExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/EditExecuteExchange.java @@ -13,7 +13,7 @@ import lombok.extern.jackson.Jacksonized; /** * Performs an edit for a data source. */ -public class EditExecuteExchange implements MessageExchange { +public class EditExecuteExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/EditPreparationExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/EditPreparationExchange.java index 4598dc998..e4d796354 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/EditPreparationExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/EditPreparationExchange.java @@ -12,23 +12,13 @@ import lombok.extern.jackson.Jacksonized; /** * Requests to edit a data source. */ -public class EditPreparationExchange implements MessageExchange { +public class EditPreparationExchange implements MessageExchange { @Override public String getId() { return "editPreparation"; } - @Override - public Class getRequestClass() { - return EditPreparationExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return EditPreparationExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchange.java index e3a63eaab..9a26f6f97 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchange.java @@ -1,13 +1,11 @@ package io.xpipe.beacon.exchange; -import io.xpipe.beacon.message.RequestMessage; -import io.xpipe.beacon.message.ResponseMessage; import lombok.SneakyThrows; /** * A message exchange scheme that implements a certain functionality. */ -public interface MessageExchange { +public interface MessageExchange { /** * The unique id of this exchange that will be included in the messages. @@ -19,10 +17,10 @@ public interface MessageExchange getRequestClass() { + default Class getRequestClass() { var c = getClass().getSuperclass(); var name = (MessageExchange.class.isAssignableFrom(c) ? c : getClass()).getName() + "$Request"; - return (Class) Class.forName(name); + return Class.forName(name); } /** @@ -30,9 +28,9 @@ public interface MessageExchange getResponseClass() { + default Class getResponseClass() { var c = getClass().getSuperclass(); var name = (MessageExchange.class.isAssignableFrom(c) ? c : getClass()).getName() + "$Response"; - return (Class) Class.forName(name); + return Class.forName(name); } } diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java b/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java index f780aceb0..8efb17bff 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/MessageExchanges.java @@ -10,37 +10,34 @@ import java.util.stream.Collectors; public class MessageExchanges { - private static Set> ALL; + private static Set ALL; private static void loadAll() { if (ALL == null) { ALL = ServiceLoader.load(MessageExchange.class).stream() - .map(s -> (MessageExchange) s.get()).collect(Collectors.toSet()); + .map(s -> (MessageExchange) s.get()).collect(Collectors.toSet()); } } - @SuppressWarnings("unchecked") - public static Optional> byId(String name) { + public static Optional byId(String name) { loadAll(); var r = ALL.stream().filter(d -> d.getId().equals(name)).findAny(); - return Optional.ofNullable((MessageExchange) r.orElse(null)); + return Optional.ofNullable((MessageExchange) r.orElse(null)); } - @SuppressWarnings("unchecked") - public static Optional> byRequest(RQ req) { + public static Optional byRequest(RQ req) { loadAll(); var r = ALL.stream().filter(d -> d.getRequestClass().equals(req.getClass())).findAny(); - return Optional.ofNullable((MessageExchange) r.orElse(null)); + return r; } - @SuppressWarnings("unchecked") - public static Optional> byResponse(RP rep) { + public static Optional byResponse(RP rep) { loadAll(); var r = ALL.stream().filter(d -> d.getResponseClass().equals(rep.getClass())).findAny(); - return Optional.ofNullable((MessageExchange) r.orElse(null)); + return r; } - public static Set> getAll() { + public static Set getAll() { loadAll(); return ALL; } diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/QueryDataSourceExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/QueryDataSourceExchange.java index fe8ef9d31..af4121b12 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/QueryDataSourceExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/QueryDataSourceExchange.java @@ -12,23 +12,13 @@ import lombok.extern.jackson.Jacksonized; /** * Queries general information about a data source. */ -public class QueryDataSourceExchange implements MessageExchange { +public class QueryDataSourceExchange implements MessageExchange { @Override public String getId() { return "queryDataSource"; } - @Override - public Class getRequestClass() { - return QueryDataSourceExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return QueryDataSourceExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/ReadExecuteExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/ReadExecuteExchange.java index 1ff067ef0..3215b551f 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/ReadExecuteExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/ReadExecuteExchange.java @@ -13,23 +13,13 @@ import lombok.extern.jackson.Jacksonized; /** * Sends stream-based data to a daemon. */ -public class ReadExecuteExchange implements MessageExchange { +public class ReadExecuteExchange implements MessageExchange { @Override public String getId() { return "readExecute"; } - @Override - public Class getRequestClass() { - return ReadExecuteExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return ReadExecuteExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/ReadPreparationExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/ReadPreparationExchange.java index d95ae0dc1..58fde9c85 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/ReadPreparationExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/ReadPreparationExchange.java @@ -3,7 +3,7 @@ package io.xpipe.beacon.exchange; import io.xpipe.beacon.message.RequestMessage; import io.xpipe.beacon.message.ResponseMessage; import io.xpipe.core.source.DataSourceConfigInstance; -import io.xpipe.core.store.StreamDataStore; +import io.xpipe.core.store.DataStore; import lombok.Builder; import lombok.NonNull; import lombok.Value; @@ -12,23 +12,13 @@ import lombok.extern.jackson.Jacksonized; /** * Prepares a client to send stream-based data to a daemon. */ -public class ReadPreparationExchange implements MessageExchange { +public class ReadPreparationExchange implements MessageExchange { @Override public String getId() { return "readPreparation"; } - @Override - public Class getRequestClass() { - return ReadPreparationExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return ReadPreparationExchange.Response.class; - } - @Jacksonized @Builder @Value @@ -36,7 +26,7 @@ public class ReadPreparationExchange implements MessageExchange { +public class RemoveCollectionExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/RemoveEntryExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/RemoveEntryExchange.java index a32f4d451..1c8972660 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/RemoveEntryExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/RemoveEntryExchange.java @@ -9,7 +9,7 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class RemoveEntryExchange implements MessageExchange { +public class RemoveEntryExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/RenameCollectionExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/RenameCollectionExchange.java index 5bf6e49f7..b5abf90bf 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/RenameCollectionExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/RenameCollectionExchange.java @@ -7,7 +7,7 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class RenameCollectionExchange implements MessageExchange { +public class RenameCollectionExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/RenameEntryExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/RenameEntryExchange.java index aa96de3d0..73f898df9 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/RenameEntryExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/RenameEntryExchange.java @@ -9,7 +9,7 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class RenameEntryExchange implements MessageExchange { +public class RenameEntryExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/StopExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/StopExchange.java index e2ea11574..d6945b69d 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/StopExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/StopExchange.java @@ -9,23 +9,13 @@ import lombok.extern.jackson.Jacksonized; /** * Requests the daemon to stop. */ -public class StopExchange implements MessageExchange { +public class StopExchange implements MessageExchange { @Override public String getId() { return "stop"; } - @Override - public Class getRequestClass() { - return StopExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return StopExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/StoreStreamExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/StoreStreamExchange.java index 754e12a64..672941a8b 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/StoreStreamExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/StoreStreamExchange.java @@ -10,23 +10,13 @@ import lombok.extern.jackson.Jacksonized; /** * Stores a stream of data in a storage. */ -public class StoreStreamExchange implements MessageExchange { +public class StoreStreamExchange implements MessageExchange { @Override public String getId() { return "storeStream"; } - @Override - public Class getRequestClass() { - return StoreStreamExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return StoreStreamExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryRawDataExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryRawDataExchange.java index 152deaf84..6e95ec48f 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryRawDataExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryRawDataExchange.java @@ -9,7 +9,7 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class QueryRawDataExchange implements MessageExchange { +public class QueryRawDataExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTableDataExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTableDataExchange.java index 6d2f7b516..af4910cfb 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTableDataExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTableDataExchange.java @@ -12,23 +12,13 @@ import lombok.extern.jackson.Jacksonized; /** * Queries data of a table data source in the xpbt format. */ -public class QueryTableDataExchange implements MessageExchange { +public class QueryTableDataExchange implements MessageExchange { @Override public String getId() { return "queryTableData"; } - @Override - public Class getRequestClass() { - return QueryTableDataExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return QueryTableDataExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTextDataExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTextDataExchange.java index d8070907c..997b1f7e7 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTextDataExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/api/QueryTextDataExchange.java @@ -9,7 +9,7 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class QueryTextDataExchange implements MessageExchange { +public class QueryTextDataExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ConvertExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ConvertExchange.java index 95ccb8a62..543763756 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ConvertExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ConvertExchange.java @@ -12,7 +12,7 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class ConvertExchange implements MessageExchange { +public class ConvertExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/DialogExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/DialogExchange.java index 587e32543..88d536bc9 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/DialogExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/DialogExchange.java @@ -3,12 +3,14 @@ package io.xpipe.beacon.exchange.cli; import io.xpipe.beacon.exchange.MessageExchange; import io.xpipe.beacon.message.RequestMessage; import io.xpipe.beacon.message.ResponseMessage; -import io.xpipe.core.source.DataSourceConfigInstance; +import io.xpipe.core.config.DialogElement; import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class DialogExchange implements MessageExchange { +import java.util.UUID; + +public class DialogExchange implements MessageExchange { @Override public String getId() { @@ -29,8 +31,7 @@ public class DialogExchange implements MessageExchange { +public class ListCollectionsExchange implements MessageExchange { @Override public String getId() { return "listCollections"; } - @Override - public Class getRequestClass() { - return Request.class; - } - - @Override - public Class getResponseClass() { - return Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ListEntriesExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ListEntriesExchange.java index 7e39701c3..1a74b3f37 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ListEntriesExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ListEntriesExchange.java @@ -10,23 +10,13 @@ import lombok.extern.jackson.Jacksonized; import java.util.List; -public class ListEntriesExchange implements MessageExchange { +public class ListEntriesExchange implements MessageExchange { @Override public String getId() { return "listEntries"; } - @Override - public Class getRequestClass() { - return Request.class; - } - - @Override - public Class getResponseClass() { - return Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ModeExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ModeExchange.java index 37ddf4bba..1d3fd204c 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ModeExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ModeExchange.java @@ -8,23 +8,13 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class ModeExchange implements MessageExchange { +public class ModeExchange implements MessageExchange { @Override public String getId() { return "mode"; } - @Override - public Class getRequestClass() { - return ModeExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return ModeExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ProviderListExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ProviderListExchange.java index 07981cfb8..4bd333119 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ProviderListExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/ProviderListExchange.java @@ -13,7 +13,7 @@ import lombok.extern.jackson.Jacksonized; import java.util.List; import java.util.Map; -public class ProviderListExchange implements MessageExchange { +public class ProviderListExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/SelectExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/SelectExchange.java index aa846e77e..0b35ae393 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/SelectExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/SelectExchange.java @@ -9,23 +9,13 @@ import lombok.NonNull; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class SelectExchange implements MessageExchange { +public class SelectExchange implements MessageExchange { @Override public String getId() { return "select"; } - @Override - public Class getRequestClass() { - return SelectExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return SelectExchange.Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StatusExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StatusExchange.java index b5958e7d8..13cc80b43 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StatusExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StatusExchange.java @@ -7,23 +7,13 @@ import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class StatusExchange implements MessageExchange { +public class StatusExchange implements MessageExchange { @Override public String getId() { return "status"; } - @Override - public Class getRequestClass() { - return Request.class; - } - - @Override - public Class getResponseClass() { - return Response.class; - } - @Jacksonized @Builder @Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StoreAddExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StoreAddExchange.java new file mode 100644 index 000000000..7f8d42574 --- /dev/null +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StoreAddExchange.java @@ -0,0 +1,36 @@ +package io.xpipe.beacon.exchange.cli; + +import io.xpipe.beacon.exchange.MessageExchange; +import io.xpipe.beacon.message.RequestMessage; +import io.xpipe.beacon.message.ResponseMessage; +import io.xpipe.core.config.DialogElement; +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +import java.util.UUID; + +public class StoreAddExchange implements MessageExchange { + + @Override + public String getId() { + return "storeAdd"; + } + + @Jacksonized + @Builder + @Value + public static class Request implements RequestMessage { + @NonNull + String input; + } + + @Jacksonized + @Builder + @Value + public static class Response implements ResponseMessage { + UUID dialogKey; + DialogElement dialogElement; + } +} diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/VersionExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/VersionExchange.java index 92a3789b6..503bf30da 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/VersionExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/VersionExchange.java @@ -7,23 +7,13 @@ import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; -public class VersionExchange implements MessageExchange { +public class VersionExchange implements MessageExchange { @Override public String getId() { return "version"; } - @Override - public Class getRequestClass() { - return VersionExchange.Request.class; - } - - @Override - public Class getResponseClass() { - return VersionExchange.Response.class; - } - @lombok.extern.jackson.Jacksonized @lombok.Builder @lombok.Value diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WriteExecuteExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WriteExecuteExchange.java index 7af483cb7..4347e9a39 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WriteExecuteExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WriteExecuteExchange.java @@ -13,7 +13,7 @@ import lombok.extern.jackson.Jacksonized; /** * Output the data source contents. */ -public class WriteExecuteExchange implements MessageExchange { +public class WriteExecuteExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WritePreparationExchange.java b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WritePreparationExchange.java index edbe4cefa..fede1de25 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WritePreparationExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/WritePreparationExchange.java @@ -14,7 +14,7 @@ import lombok.extern.jackson.Jacksonized; /** * Prepares a client to output the data source contents. */ -public class WritePreparationExchange implements MessageExchange { +public class WritePreparationExchange implements MessageExchange { @Override public String getId() { diff --git a/beacon/src/main/java/module-info.java b/beacon/src/main/java/module-info.java index 8686ab5c9..8fb129e08 100644 --- a/beacon/src/main/java/module-info.java +++ b/beacon/src/main/java/module-info.java @@ -29,6 +29,7 @@ module io.xpipe.beacon { ModeExchange, StatusExchange, StopExchange, + StoreAddExchange, WritePreparationExchange, WriteExecuteExchange, SelectExchange, diff --git a/core/src/main/java/io/xpipe/core/config/BaseQueryElement.java b/core/src/main/java/io/xpipe/core/config/BaseQueryElement.java new file mode 100644 index 000000000..7bc603d0d --- /dev/null +++ b/core/src/main/java/io/xpipe/core/config/BaseQueryElement.java @@ -0,0 +1,21 @@ +package io.xpipe.core.config; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Getter; + +@JsonTypeName("query") +@Getter +public class BaseQueryElement extends DialogElement { + + private final String description; + private final boolean required; + protected String value; + + @JsonCreator + public BaseQueryElement(String description, boolean required, String value) { + this.description = description; + this.required = required; + this.value = value; + } +} diff --git a/core/src/main/java/io/xpipe/core/config/ChoiceElement.java b/core/src/main/java/io/xpipe/core/config/ChoiceElement.java new file mode 100644 index 000000000..86d3f3abf --- /dev/null +++ b/core/src/main/java/io/xpipe/core/config/ChoiceElement.java @@ -0,0 +1,67 @@ +package io.xpipe.core.config; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Value; +import lombok.extern.jackson.Jacksonized; + +import java.util.List; + +@JsonTypeName("choice") +public class ChoiceElement extends DialogElement { + + private final List elements; + + private int selected; + + @Override + public boolean apply(String value) { + if (value == null) { + return true; + } + + if (value.length() != 1) { + return true; + } + + var c = value.charAt(0); + if (Character.isDigit(c)) { + selected = Integer.parseInt(value) - 1; + return true; + } + + for (int i = 0; i < elements.size(); i++) { + if (elements.get(i).getCharacter() != null && elements.get(i).getCharacter().equals(c)) { + selected = i; + return true; + } + } + + return false; + } + + @Value + @Builder + @Jacksonized + @AllArgsConstructor + public static class Element { + Character character; + String description; + } + + @JsonCreator + public ChoiceElement(List elements, int selected) { + this.elements = elements; + this.selected = selected; + } + + public List getElements() { + return elements; + } + + public int getSelected() { + return selected; + } +} diff --git a/core/src/main/java/io/xpipe/core/config/ConfigParameter.java b/core/src/main/java/io/xpipe/core/config/ConfigParameter.java index 3190a8081..43fd0e372 100644 --- a/core/src/main/java/io/xpipe/core/config/ConfigParameter.java +++ b/core/src/main/java/io/xpipe/core/config/ConfigParameter.java @@ -17,10 +17,10 @@ public class ConfigParameter { } @JsonIgnore - ConfigConverter converter; + QueryConverter converter; @SuppressWarnings("unchecked") - public ConfigConverter getConverter() { - return (ConfigConverter) converter; + public QueryConverter getConverter() { + return (QueryConverter) converter; } } diff --git a/core/src/main/java/io/xpipe/core/config/Dialog.java b/core/src/main/java/io/xpipe/core/config/Dialog.java new file mode 100644 index 000000000..7287394bd --- /dev/null +++ b/core/src/main/java/io/xpipe/core/config/Dialog.java @@ -0,0 +1,176 @@ +package io.xpipe.core.config; + +import java.util.function.Function; +import java.util.function.Supplier; + +public abstract class Dialog { + + private static class Sequence extends Dialog { + + private int index = 0; + private final DialogElement[] es; + + public Sequence(DialogElement... es) { + this.es = es; + } + + @Override + public DialogElement start() { + index = 0; + return es[0]; + } + + @Override + public DialogElement receive(String answer) { + if (es[index].apply(answer)) { + if (index == es.length - 1) { + complete(); + return null; + } else { + return es[++index]; + } + } + + return es[index]; + } + } + + public static Dialog chain(DialogElement... es) { + return new Dialog.Sequence(es); + } + + public static Dialog chain(Dialog... ds) { + return new Dialog() { + + private int current = 0; + + @Override + public DialogElement start() { + current = 0; + return ds[0].start(); + } + + @Override + public DialogElement receive(String answer) { + DialogElement currentElement = ds[current].receive(answer); + if (currentElement == null) { + ds[current].complete(); + if (current == ds.length - 1) { + complete(); + return null; + } else { + return ds[++current].start(); + } + } + + return currentElement; + } + }; + } + + public static Dialog of(DialogElement e) { + return new Dialog() { + + + @Override + public DialogElement start() { + return e; + } + + @Override + public DialogElement receive(String answer) { + if (e.apply(answer)) { + complete(); + return null; + } + + return e; + } + }; + } + + public static Dialog retryIf(Dialog d, Supplier msg) { + return new Dialog() { + + private boolean retry; + + @Override + public DialogElement start() { + return d.start(); + } + + @Override + public DialogElement receive(String answer) { + if (retry) { + retry = false; + return d.start(); + } + + var next = d.receive(answer); + if (next == null) { + var s = msg.get(); + if (s != null) { + retry = true; + return new HeaderElement(s); + } + } + + return next; + } + }.evaluateTo(d.onCompletion); + } + + public static Dialog choice(ChoiceElement choice, Function c) { + return new Dialog() { + + private Dialog choiceMade; + + @Override + public DialogElement start() { + choiceMade = null; + return choice; + } + + @Override + public DialogElement receive(String answer) { + if (choiceMade != null) { + var r = choiceMade.receive(answer); + if (r == null) { + complete(); + } + return r; + } + + if (choice.apply(answer)) { + choiceMade = c.apply(choice.getSelected()); + return choiceMade.start(); + } + + return choice; + } + }; + } + + private Object eval; + private Supplier onCompletion; + + public abstract DialogElement start(); + + public Dialog evaluateTo(Supplier s) { + onCompletion = s; + return this; + } + + @SuppressWarnings("unchecked") + public T getResult() { + return (T) eval; + } + + public void complete() { + if (onCompletion != null) { + eval = onCompletion.get(); + } + } + + public abstract DialogElement receive(String answer); +} diff --git a/core/src/main/java/io/xpipe/core/config/DialogElement.java b/core/src/main/java/io/xpipe/core/config/DialogElement.java new file mode 100644 index 000000000..78eb7ede5 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/config/DialogElement.java @@ -0,0 +1,25 @@ +package io.xpipe.core.config; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.EqualsAndHashCode; + +import java.util.UUID; + +@EqualsAndHashCode +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +public abstract class DialogElement { + + protected String id; + + public DialogElement() { + this.id = UUID.randomUUID().toString(); + } + + public boolean apply(String value) { + throw new UnsupportedOperationException(); + } + + public String getId() { + return id; + } +} diff --git a/core/src/main/java/io/xpipe/core/config/HeaderElement.java b/core/src/main/java/io/xpipe/core/config/HeaderElement.java new file mode 100644 index 000000000..0710d5e2a --- /dev/null +++ b/core/src/main/java/io/xpipe/core/config/HeaderElement.java @@ -0,0 +1,24 @@ +package io.xpipe.core.config; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; + +@JsonTypeName("header") +public class HeaderElement extends DialogElement { + + protected String header; + + @JsonCreator + public HeaderElement(String header) { + this.header = header; + } + + @Override + public boolean apply(String value) { + return true; + } + + public String getHeader() { + return header; + } +} diff --git a/core/src/main/java/io/xpipe/core/config/ConfigConverter.java b/core/src/main/java/io/xpipe/core/config/QueryConverter.java similarity index 71% rename from core/src/main/java/io/xpipe/core/config/ConfigConverter.java rename to core/src/main/java/io/xpipe/core/config/QueryConverter.java index e6f67bdca..f4f0983b3 100644 --- a/core/src/main/java/io/xpipe/core/config/ConfigConverter.java +++ b/core/src/main/java/io/xpipe/core/config/QueryConverter.java @@ -2,9 +2,9 @@ package io.xpipe.core.config; import java.nio.charset.Charset; -public abstract class ConfigConverter { +public abstract class QueryConverter { - public static final ConfigConverter CHARSET = new ConfigConverter() { + public static final QueryConverter CHARSET = new QueryConverter() { @Override protected Charset fromString(String s) { return Charset.forName(s); @@ -16,7 +16,7 @@ public abstract class ConfigConverter { } }; - public static final ConfigConverter STRING = new ConfigConverter() { + public static final QueryConverter STRING = new QueryConverter() { @Override protected String fromString(String s) { return s; @@ -28,7 +28,19 @@ public abstract class ConfigConverter { } }; - public static final ConfigConverter CHARACTER = new ConfigConverter() { + public static final QueryConverter INTEGER = new QueryConverter() { + @Override + protected Integer fromString(String s) { + return Integer.parseInt(s); + } + + @Override + protected String toString(Integer value) { + return value.toString(); + } + }; + + public static final QueryConverter CHARACTER = new QueryConverter() { @Override protected Character fromString(String s) { if (s.length() != 1) { @@ -44,7 +56,7 @@ public abstract class ConfigConverter { } }; - public static final ConfigConverter BOOLEAN = new ConfigConverter() { + public static final QueryConverter BOOLEAN = new QueryConverter() { @Override protected Boolean fromString(String s) { if (s.equalsIgnoreCase("y") || s.equalsIgnoreCase("yes") || s.equalsIgnoreCase("true")) { diff --git a/core/src/main/java/io/xpipe/core/config/QueryElement.java b/core/src/main/java/io/xpipe/core/config/QueryElement.java new file mode 100644 index 000000000..f432f9650 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/config/QueryElement.java @@ -0,0 +1,47 @@ +package io.xpipe.core.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +@JsonSerialize(as = BaseQueryElement.class) +public class QueryElement extends BaseQueryElement { + + @JsonIgnore + private final QueryConverter converter; + + public QueryElement(String description, boolean required, String value, QueryConverter converter) { + super(description, required, value); + this.converter = converter; + } + + public QueryElement(String description, boolean required, Object value, QueryConverter converter) { + super(description, required, value != null ? value.toString() : null); + this.converter = converter; + } + + @Override + public boolean apply(String value) { + if (value == null && this.value != null) { + if (isRequired() && this.value == null) { + return false; + } + + this.value = null; + return true; + } + + try { + converter.convertFromString(value); + } catch (Exception ex) { + return false; + } + + this.value = value; + return true; + } + + @SuppressWarnings("unchecked") + public T getConvertedValue() { + return (T) converter.convertFromString(value); + } +} 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 55a9e06c3..01c686a66 100644 --- a/core/src/main/java/io/xpipe/core/source/DataSource.java +++ b/core/src/main/java/io/xpipe/core/source/DataSource.java @@ -38,8 +38,12 @@ public abstract class DataSource { return c; } - public boolean isComplete() { - return true; + protected boolean supportsRead() { + return false; + } + + protected boolean supportsWrite() { + return false; } /** @@ -65,9 +69,13 @@ public abstract class DataSource { */ public abstract DataSourceInfo determineInfo() throws Exception; - public abstract DataSourceReadConnection openReadConnection() throws Exception; + public DataSourceReadConnection openReadConnection() throws Exception { + throw new UnsupportedOperationException(); + } - public abstract DataSourceConnection openWriteConnection() throws Exception; + public DataSourceConnection openWriteConnection() throws Exception { + throw new UnsupportedOperationException(); + } public DataSourceConnection openAppendingWriteConnection() throws Exception { throw new UnsupportedOperationException("Appending write is not supported"); diff --git a/core/src/main/java/io/xpipe/core/source/JdbcQuerySource.java b/core/src/main/java/io/xpipe/core/source/JdbcQuerySource.java new file mode 100644 index 000000000..691830982 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/source/JdbcQuerySource.java @@ -0,0 +1,94 @@ +package io.xpipe.core.source; + +import io.xpipe.core.data.node.*; +import io.xpipe.core.data.type.TupleType; +import io.xpipe.core.data.type.ValueType; +import io.xpipe.core.store.JdbcStore; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; + +@Data +@EqualsAndHashCode(callSuper = true) +public abstract class JdbcQuerySource extends TableDataSource { + + JdbcStore store; + + protected abstract String createQuery(); + + public Connection createConnection() throws SQLException { + return store.createConnection(); + } + + @Override + protected boolean supportsRead() { + return true; + } + + @Override + protected TableReadConnection newReadConnection() { + return new TableReadConnection() { + + private Connection connection; + private Statement statement; + private TupleType dataType; + private ResultSet resultSet; + + @Override + public void init() throws Exception { + connection = createConnection(); + statement = connection.createStatement(); + + resultSet = statement.executeQuery(createQuery()); + var meta = resultSet.getMetaData(); + var names = new ArrayList(); + for (int i = 0; i < meta.getColumnCount(); i++) { + names.add(meta.getColumnName(i + 1)); + } + dataType = TupleType.of(names, Collections.nCopies(names.size(), ValueType.of())); + } + + @Override + public void close() throws Exception { + statement.close(); + connection.close(); + } + + @Override + public TupleType getDataType() { + return dataType; + } + + @Override + public int getRowCount() throws Exception { + return resultSet.getFetchSize(); + } + + @Override + public void withRows(DataStructureNodeAcceptor lineAcceptor) throws Exception { + while (resultSet.next()) { + var vals = new ArrayList(); + for (int i = 0; i < dataType.getSize(); i++) { + vals.add(ValueNode.of(resultSet.getString(i))); + } + + var node = TupleNode.of(dataType.getNames(), vals); + if (!lineAcceptor.accept(node)) { + break; + } + } + } + + @Override + public ArrayNode readRows(int maxLines) throws Exception { + return null; + } + }; + } +} diff --git a/core/src/main/java/io/xpipe/core/source/TableReadConnection.java b/core/src/main/java/io/xpipe/core/source/TableReadConnection.java index 0227f6601..ed39a73c3 100644 --- a/core/src/main/java/io/xpipe/core/source/TableReadConnection.java +++ b/core/src/main/java/io/xpipe/core/source/TableReadConnection.java @@ -18,7 +18,7 @@ public interface TableReadConnection extends DataSourceReadConnection { public static TableReadConnection empty() { return new TableReadConnection() { @Override - public TupleType getDataType() throws Exception { + public TupleType getDataType() { return TupleType.empty(); } @@ -42,7 +42,7 @@ public interface TableReadConnection extends DataSourceReadConnection { /** * Returns the data type of the table data. */ - TupleType getDataType() throws Exception; + TupleType getDataType(); /** * Returns the amount of rows to be read or -1 if the amount is unknown. diff --git a/core/src/main/java/io/xpipe/core/store/CommandInputStore.java b/core/src/main/java/io/xpipe/core/store/CommandInputStore.java new file mode 100644 index 000000000..eb6851485 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/store/CommandInputStore.java @@ -0,0 +1,19 @@ +package io.xpipe.core.store; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Value; + +import java.io.InputStream; + +@Value +@JsonTypeName("commandInput") +public class CommandInputStore implements StreamDataStore { + + String cmd; + + @Override + public InputStream openInput() throws Exception { + var proc = Runtime.getRuntime().exec(cmd); + return proc.getInputStream(); + } +} diff --git a/core/src/main/java/io/xpipe/core/store/HttpRequestStore.java b/core/src/main/java/io/xpipe/core/store/HttpRequestStore.java new file mode 100644 index 000000000..0506d34d7 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/store/HttpRequestStore.java @@ -0,0 +1,52 @@ +package io.xpipe.core.store; + +import com.fasterxml.jackson.annotation.JsonTypeName; +import lombok.Value; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Map; +import java.util.Optional; + +@Value +@JsonTypeName("httpRequest") +public class HttpRequestStore implements StreamDataStore { + + public static boolean isHttpRequest(String s) { + return s.startsWith("http:") || s.startsWith("https:"); + } + + public static Optional fromString(String s) { + try { + var uri = new URI(s); + return Optional.of(new HttpRequestStore(uri, Map.of())); + } catch (URISyntaxException e) { + return Optional.empty(); + } + } + + URI uri; + Map headers; + + @Override + public InputStream openInput() throws Exception { + var b = HttpRequest.newBuilder().uri(uri); + headers.forEach(b::setHeader); + var req = b.GET().build(); + + var client = HttpClient.newHttpClient(); + var res = client.send(req, HttpResponse.BodyHandlers.ofByteArray()); + + return new ByteArrayInputStream(res.body()); + } + + @Override + public boolean exists() { + return false; + } +} diff --git a/core/src/main/java/io/xpipe/core/store/JdbcStore.java b/core/src/main/java/io/xpipe/core/store/JdbcStore.java new file mode 100644 index 000000000..523e7cafd --- /dev/null +++ b/core/src/main/java/io/xpipe/core/store/JdbcStore.java @@ -0,0 +1,34 @@ +package io.xpipe.core.store; + +import lombok.*; +import lombok.experimental.FieldDefaults; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; + +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +@Getter +@EqualsAndHashCode +@ToString +@AllArgsConstructor +public abstract class JdbcStore implements DataStore { + + String hostname; + int port; + + public void checkConnect() throws Exception { + try (Connection con = createConnection()) { + return; + } + } + + public Connection createConnection() throws SQLException { + return DriverManager.getConnection(toUrl(), toProperties()); + } + + public abstract String toUrl(); + + public abstract Properties toProperties(); +} diff --git a/core/src/main/java/io/xpipe/core/store/StreamDataStore.java b/core/src/main/java/io/xpipe/core/store/StreamDataStore.java index e73c3b652..6bcfe55f7 100644 --- a/core/src/main/java/io/xpipe/core/store/StreamDataStore.java +++ b/core/src/main/java/io/xpipe/core/store/StreamDataStore.java @@ -49,7 +49,9 @@ public interface StreamDataStore extends DataStore { throw new UnsupportedOperationException("Can't open store output"); } - boolean exists(); + default boolean exists() { + return false; + } default boolean persistent() { return false; diff --git a/core/src/main/java/io/xpipe/core/util/CoreJacksonModule.java b/core/src/main/java/io/xpipe/core/util/CoreJacksonModule.java index cd3f87caa..dcdb9d883 100644 --- a/core/src/main/java/io/xpipe/core/util/CoreJacksonModule.java +++ b/core/src/main/java/io/xpipe/core/util/CoreJacksonModule.java @@ -11,6 +11,9 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.module.SimpleModule; +import io.xpipe.core.config.BaseQueryElement; +import io.xpipe.core.config.ChoiceElement; +import io.xpipe.core.config.HeaderElement; import io.xpipe.core.data.type.ArrayType; import io.xpipe.core.data.type.TupleType; import io.xpipe.core.data.type.ValueType; @@ -18,6 +21,7 @@ import io.xpipe.core.data.type.WildcardType; import io.xpipe.core.source.DataSourceInfo; import io.xpipe.core.source.DataSourceReference; import io.xpipe.core.store.CollectionEntryDataStore; +import io.xpipe.core.store.HttpRequestStore; import io.xpipe.core.store.LocalDirectoryDataStore; import io.xpipe.core.store.LocalFileDataStore; @@ -33,6 +37,7 @@ public class CoreJacksonModule extends SimpleModule { new NamedType(LocalFileDataStore.class), new NamedType(LocalDirectoryDataStore.class), new NamedType(CollectionEntryDataStore.class), + new NamedType(HttpRequestStore.class), new NamedType(ValueType.class), new NamedType(TupleType.class), new NamedType(ArrayType.class), @@ -41,7 +46,10 @@ public class CoreJacksonModule extends SimpleModule { new NamedType(DataSourceInfo.Structure.class), new NamedType(DataSourceInfo.Text.class), new NamedType(DataSourceInfo.Collection.class), - new NamedType(DataSourceInfo.Raw.class) + new NamedType(DataSourceInfo.Raw.class), + new NamedType(BaseQueryElement.class), + new NamedType(ChoiceElement.class), + new NamedType(HeaderElement.class) ); addSerializer(Charset.class, new CharsetSerializer()); diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index e1608501b..d1141e8d9 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -22,7 +22,9 @@ module io.xpipe.core { requires com.fasterxml.jackson.core; requires com.fasterxml.jackson.databind; + requires java.net.http; requires static lombok; + requires java.sql; uses com.fasterxml.jackson.databind.Module; provides com.fasterxml.jackson.databind.Module with CoreJacksonModule; diff --git a/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java b/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java index c92d4f48f..45b6894ce 100644 --- a/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java +++ b/extension/src/main/java/io/xpipe/extension/DataSourceProvider.java @@ -1,7 +1,7 @@ package io.xpipe.extension; import io.xpipe.charsetter.NewLine; -import io.xpipe.core.config.ConfigConverter; +import io.xpipe.core.config.QueryConverter; import io.xpipe.core.config.ConfigParameter; import io.xpipe.core.source.DataSource; import io.xpipe.core.source.DataSourceType; @@ -9,7 +9,6 @@ import io.xpipe.core.store.DataStore; import javafx.beans.property.Property; import javafx.scene.layout.Region; import lombok.SneakyThrows; -import lombok.Value; import java.util.Arrays; import java.util.List; @@ -93,13 +92,6 @@ public interface DataSourceProvider> { interface ConfigProvider> { - @Value - static class Parameter { - ConfigParameter parameter; - Object currentValue; - boolean required; - } - static > ConfigProvider empty(List names, Function func) { return new ConfigProvider<>() { @Override @@ -124,9 +116,9 @@ public interface DataSourceProvider> { } ConfigParameter CHARSET = new ConfigParameter( - "charset", ConfigConverter.CHARSET); + "charset", QueryConverter.CHARSET); - public static final ConfigConverter NEW_LINE_CONVERTER = new ConfigConverter() { + public static final QueryConverter NEW_LINE_CONVERTER = new QueryConverter() { @Override protected NewLine fromString(String s) { return NewLine.id(s); diff --git a/extension/src/main/java/io/xpipe/extension/DataStoreProvider.java b/extension/src/main/java/io/xpipe/extension/DataStoreProvider.java new file mode 100644 index 000000000..e657d1bc3 --- /dev/null +++ b/extension/src/main/java/io/xpipe/extension/DataStoreProvider.java @@ -0,0 +1,46 @@ +package io.xpipe.extension; + +import io.xpipe.core.config.Dialog; + +import java.net.URL; +import java.util.List; + +public interface DataStoreProvider { + + default void init() throws Exception { + } + + default String i18n(String key) { + return I18n.get(getId() + "." + key); + } + + default String i18nKey(String key) { + return getId() + "." + key; + } + + default String getDisplayName() { + return i18n("displayName"); + } + + default String getDisplayDescription() { + return i18n("displayDescription"); + } + + default String getDisplayIconFileName() { + return getId() + ":icon.png"; + } + + default Dialog dialogForURL(URL url) { + return null; + } + + Dialog defaultDialog(); + + List getPossibleNames(); + + default String getId() { + var n = getClass().getPackageName(); + var i = n.lastIndexOf('.'); + return i != -1 ? n.substring(i + 1) : n; + } +} diff --git a/extension/src/main/java/io/xpipe/extension/DataStoreProviders.java b/extension/src/main/java/io/xpipe/extension/DataStoreProviders.java new file mode 100644 index 000000000..5b006c74d --- /dev/null +++ b/extension/src/main/java/io/xpipe/extension/DataStoreProviders.java @@ -0,0 +1,51 @@ +package io.xpipe.extension; + +import io.xpipe.core.config.Dialog; +import io.xpipe.extension.event.ErrorEvent; + +import java.net.URL; +import java.util.Objects; +import java.util.Optional; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.stream.Collectors; + +public class DataStoreProviders { + + private static Set ALL; + + public static void init(ModuleLayer layer) { + if (ALL == null) { + ALL = ServiceLoader.load(layer, DataStoreProvider.class).stream() + .map(p -> (DataStoreProvider) p.get()).collect(Collectors.toSet()); + ALL.forEach(p -> { + try { + p.init(); + } catch (Exception e) { + ErrorEvent.fromThrowable(e).handle(); + } + }); + } + } + + public static Optional byName(String name) { + if (ALL == null) { + throw new IllegalStateException("Not initialized"); + } + + return ALL.stream().filter(d -> d.getPossibleNames().stream() + .anyMatch(s -> s.equalsIgnoreCase(name))).findAny(); + } + + public static Optional byURL(URL url) { + if (ALL == null) { + throw new IllegalStateException("Not initialized"); + } + + return ALL.stream().map(d -> d.dialogForURL(url)).filter(Objects::nonNull).findAny(); + } + + public static Set getAll() { + return ALL; + } +} diff --git a/extension/src/main/java/module-info.java b/extension/src/main/java/module-info.java index 5f068e640..e461d08c2 100644 --- a/extension/src/main/java/module-info.java +++ b/extension/src/main/java/module-info.java @@ -35,4 +35,5 @@ module io.xpipe.extension { uses io.xpipe.extension.I18n; uses io.xpipe.extension.event.EventHandler; uses io.xpipe.extension.prefs.PrefsProvider; + uses io.xpipe.extension.DataStoreProvider; } \ No newline at end of file