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 88d536bc..9557790a 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,7 +3,7 @@ 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 io.xpipe.core.dialog.DialogElement; import lombok.Builder; import lombok.Value; import lombok.extern.jackson.Jacksonized; 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 index 7f8d4257..43353f77 100644 --- a/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StoreAddExchange.java +++ b/beacon/src/main/java/io/xpipe/beacon/exchange/cli/StoreAddExchange.java @@ -3,7 +3,7 @@ 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 io.xpipe.core.dialog.DialogElement; import lombok.Builder; import lombok.NonNull; import lombok.Value; diff --git a/core/src/main/java/io/xpipe/core/data/node/SimpleImmutableValueNode.java b/core/src/main/java/io/xpipe/core/data/node/SimpleImmutableValueNode.java index e921aa7c..1b8179a9 100644 --- a/core/src/main/java/io/xpipe/core/data/node/SimpleImmutableValueNode.java +++ b/core/src/main/java/io/xpipe/core/data/node/SimpleImmutableValueNode.java @@ -26,6 +26,10 @@ public class SimpleImmutableValueNode extends ImmutableValueNode { @Override public final String asString() { + if (getRawData() == null) { + return "null"; + } + return new String(getRawData()); } } 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 85b79ef6..fcc41926 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 @@ -34,6 +34,10 @@ public abstract class ValueNode extends DataStructureNode { } public static ValueNode immutable(Object o, boolean textual) { + if (o == null) { + return immutableNull(); + } + return immutable(o.toString().getBytes(StandardCharsets.UTF_8), textual); } diff --git a/core/src/main/java/io/xpipe/core/config/BaseQueryElement.java b/core/src/main/java/io/xpipe/core/dialog/BaseQueryElement.java similarity index 94% rename from core/src/main/java/io/xpipe/core/config/BaseQueryElement.java rename to core/src/main/java/io/xpipe/core/dialog/BaseQueryElement.java index 7bc603d0..ce5eab80 100644 --- a/core/src/main/java/io/xpipe/core/config/BaseQueryElement.java +++ b/core/src/main/java/io/xpipe/core/dialog/BaseQueryElement.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; diff --git a/core/src/main/java/io/xpipe/core/config/ChoiceElement.java b/core/src/main/java/io/xpipe/core/dialog/ChoiceElement.java similarity index 98% rename from core/src/main/java/io/xpipe/core/config/ChoiceElement.java rename to core/src/main/java/io/xpipe/core/dialog/ChoiceElement.java index 86d3f3ab..de8a8a0c 100644 --- a/core/src/main/java/io/xpipe/core/config/ChoiceElement.java +++ b/core/src/main/java/io/xpipe/core/dialog/ChoiceElement.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; diff --git a/core/src/main/java/io/xpipe/core/config/ConfigParameter.java b/core/src/main/java/io/xpipe/core/dialog/ConfigParameter.java similarity index 94% rename from core/src/main/java/io/xpipe/core/config/ConfigParameter.java rename to core/src/main/java/io/xpipe/core/dialog/ConfigParameter.java index 43fd0e37..c242c281 100644 --- a/core/src/main/java/io/xpipe/core/config/ConfigParameter.java +++ b/core/src/main/java/io/xpipe/core/dialog/ConfigParameter.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/core/src/main/java/io/xpipe/core/config/ConfigParameterSetInstance.java b/core/src/main/java/io/xpipe/core/dialog/ConfigParameterSetInstance.java similarity index 98% rename from core/src/main/java/io/xpipe/core/config/ConfigParameterSetInstance.java rename to core/src/main/java/io/xpipe/core/dialog/ConfigParameterSetInstance.java index 481f2693..3cdae12d 100644 --- a/core/src/main/java/io/xpipe/core/config/ConfigParameterSetInstance.java +++ b/core/src/main/java/io/xpipe/core/dialog/ConfigParameterSetInstance.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import com.fasterxml.jackson.annotation.JsonCreator; import lombok.AllArgsConstructor; diff --git a/core/src/main/java/io/xpipe/core/config/Dialog.java b/core/src/main/java/io/xpipe/core/dialog/Dialog.java similarity index 87% rename from core/src/main/java/io/xpipe/core/config/Dialog.java rename to core/src/main/java/io/xpipe/core/dialog/Dialog.java index 7287394b..7372c0af 100644 --- a/core/src/main/java/io/xpipe/core/config/Dialog.java +++ b/core/src/main/java/io/xpipe/core/dialog/Dialog.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import java.util.function.Function; import java.util.function.Supplier; @@ -68,6 +68,29 @@ public abstract class Dialog { }; } + public static Dialog repeatIf(Dialog d, Supplier shouldRepeat) { + return new Dialog() { + + + @Override + public DialogElement start() { + return d.start(); + } + + @Override + public DialogElement receive(String answer) { + var next = d.receive(answer); + if (next == null) { + if (shouldRepeat.get()) { + return d.start(); + } + } + + return next; + } + }.evaluateTo(d.onCompletion); + } + public static Dialog of(DialogElement e) { return new Dialog() { diff --git a/core/src/main/java/io/xpipe/core/config/DialogElement.java b/core/src/main/java/io/xpipe/core/dialog/DialogElement.java similarity index 94% rename from core/src/main/java/io/xpipe/core/config/DialogElement.java rename to core/src/main/java/io/xpipe/core/dialog/DialogElement.java index 78eb7ede..6e85b0ac 100644 --- a/core/src/main/java/io/xpipe/core/config/DialogElement.java +++ b/core/src/main/java/io/xpipe/core/dialog/DialogElement.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.EqualsAndHashCode; diff --git a/core/src/main/java/io/xpipe/core/config/HeaderElement.java b/core/src/main/java/io/xpipe/core/dialog/HeaderElement.java similarity index 93% rename from core/src/main/java/io/xpipe/core/config/HeaderElement.java rename to core/src/main/java/io/xpipe/core/dialog/HeaderElement.java index 0710d5e2..81d3c499 100644 --- a/core/src/main/java/io/xpipe/core/config/HeaderElement.java +++ b/core/src/main/java/io/xpipe/core/dialog/HeaderElement.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonTypeName; diff --git a/core/src/main/java/io/xpipe/core/config/QueryConverter.java b/core/src/main/java/io/xpipe/core/dialog/QueryConverter.java similarity index 82% rename from core/src/main/java/io/xpipe/core/config/QueryConverter.java rename to core/src/main/java/io/xpipe/core/dialog/QueryConverter.java index f4f0983b..f70567b7 100644 --- a/core/src/main/java/io/xpipe/core/config/QueryConverter.java +++ b/core/src/main/java/io/xpipe/core/dialog/QueryConverter.java @@ -1,5 +1,7 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; +import java.net.MalformedURLException; +import java.net.URL; import java.nio.charset.Charset; public abstract class QueryConverter { @@ -28,6 +30,22 @@ public abstract class QueryConverter { } }; + public static final QueryConverter URL = new QueryConverter() { + @Override + protected URL fromString(String s) { + try { + return new URL(s); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + protected String toString(URL value) { + return value.toString(); + } + }; + public static final QueryConverter INTEGER = new QueryConverter() { @Override protected Integer fromString(String s) { diff --git a/core/src/main/java/io/xpipe/core/config/QueryElement.java b/core/src/main/java/io/xpipe/core/dialog/QueryElement.java similarity index 97% rename from core/src/main/java/io/xpipe/core/config/QueryElement.java rename to core/src/main/java/io/xpipe/core/dialog/QueryElement.java index f432f965..439c71d6 100644 --- a/core/src/main/java/io/xpipe/core/config/QueryElement.java +++ b/core/src/main/java/io/xpipe/core/dialog/QueryElement.java @@ -1,4 +1,4 @@ -package io.xpipe.core.config; +package io.xpipe.core.dialog; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.annotation.JsonSerialize; diff --git a/core/src/main/java/io/xpipe/core/source/DataSourceConfigInstance.java b/core/src/main/java/io/xpipe/core/source/DataSourceConfigInstance.java index 3c5923e9..57276d83 100644 --- a/core/src/main/java/io/xpipe/core/source/DataSourceConfigInstance.java +++ b/core/src/main/java/io/xpipe/core/source/DataSourceConfigInstance.java @@ -1,8 +1,8 @@ package io.xpipe.core.source; import com.fasterxml.jackson.annotation.JsonCreator; -import io.xpipe.core.config.ConfigParameter; -import io.xpipe.core.config.ConfigParameterSetInstance; +import io.xpipe.core.dialog.ConfigParameter; +import io.xpipe.core.dialog.ConfigParameterSetInstance; import lombok.AllArgsConstructor; import lombok.Value; diff --git a/core/src/main/java/io/xpipe/core/source/JdbcQuerySource.java b/core/src/main/java/io/xpipe/core/source/JdbcQuerySource.java deleted file mode 100644 index 69183098..00000000 --- a/core/src/main/java/io/xpipe/core/source/JdbcQuerySource.java +++ /dev/null @@ -1,94 +0,0 @@ -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/TableDataSource.java b/core/src/main/java/io/xpipe/core/source/TableDataSource.java index 124f8d5f..8c221ede 100644 --- a/core/src/main/java/io/xpipe/core/source/TableDataSource.java +++ b/core/src/main/java/io/xpipe/core/source/TableDataSource.java @@ -41,9 +41,15 @@ public abstract class TableDataSource extends DataSource (DataStoreProvider) p.get()).collect(Collectors.toSet()); + .map(ServiceLoader.Provider::get).collect(Collectors.toSet()); ALL.forEach(p -> { try { p.init(); diff --git a/extension/src/main/java/io/xpipe/extension/comp/DynamicOptionsBuilder.java b/extension/src/main/java/io/xpipe/extension/comp/DynamicOptionsBuilder.java index 068ebed5..4f68dd5c 100644 --- a/extension/src/main/java/io/xpipe/extension/comp/DynamicOptionsBuilder.java +++ b/extension/src/main/java/io/xpipe/extension/comp/DynamicOptionsBuilder.java @@ -25,14 +25,6 @@ public class DynamicOptionsBuilder> { private final List entries = new ArrayList<>(); private final List> props = new ArrayList<>(); - public DynamicOptionsBuilder addText(ObservableValue name, Property prop) { - var comp = new TextField(); - comp.textProperty().bindBidirectional(prop); - entries.add(new DynamicOptionsComp.Entry(name, Comp.of(() -> comp))); - props.add(prop); - return this; - } - public DynamicOptionsBuilder addNewLine(Property prop) { var map = new LinkedHashMap>(); for (var e : NewLine.values()) { @@ -72,6 +64,19 @@ public class DynamicOptionsBuilder> { return this; } + public DynamicOptionsBuilder addString(ObservableValue name, Property prop) { + var comp = Comp.of(() -> { + var tf = new TextField(prop.getValue()); + tf.textProperty().addListener((c, o, n) -> { + prop.setValue(n.length() > 0 ? n : null); + }); + return tf; + }); + entries.add(new DynamicOptionsComp.Entry(name, comp)); + props.add(prop); + return this; + } + public Region build(Function creator, Property toBind) { var bind = Bindings.createObjectBinding(() -> creator.apply(toBind.getValue()), props.toArray(Observable[]::new)); bind.addListener((c,o, n) -> {