Implement more data types and various fixes

This commit is contained in:
Christopher Schnick 2022-11-04 13:33:06 +01:00
parent 2cb3670b5b
commit 17feaf8a51
25 changed files with 306 additions and 75 deletions

View file

@ -71,8 +71,9 @@ public class DataTableImpl extends DataSourceImpl implements DataTable {
.maxRows(maxRows)
.build();
con.performInputExchange(req, (QueryTableDataExchange.Response res, InputStream in) -> {
var r = new TypedDataStreamParser(info.getDataType());
r.parseStructures(in, TypedDataStructureNodeReader.of(info.getDataType()), nodes::add);
var r = new TypedDataStreamParser(res.getDataType());
r.parseStructures(in, TypedDataStructureNodeReader.of(res.getDataType()), nodes::add);
});
});
return ArrayNode.of(nodes);

View file

@ -3,6 +3,7 @@ package io.xpipe.beacon.exchange.api;
import io.xpipe.beacon.RequestMessage;
import io.xpipe.beacon.ResponseMessage;
import io.xpipe.beacon.exchange.MessageExchange;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.source.DataSourceReference;
import lombok.Builder;
import lombok.NonNull;
@ -32,5 +33,7 @@ public class QueryTableDataExchange implements MessageExchange {
@Jacksonized
@Builder
@Value
public static class Response implements ResponseMessage {}
public static class Response implements ResponseMessage {
@NonNull TupleType dataType;
}
}

View file

@ -17,12 +17,18 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
public static final Integer INTEGER_VALUE = 6;
public static final Integer IS_NULL = 7;
public static final Integer IS_INTEGER = 9;
public static final Integer IS_FLOATING_POINT = 10;
public static final Integer FLOATING_POINT_VALUE = 11;
public static final Integer IS_DECIMAL = 10;
public static final Integer DECIMAL_VALUE = 11;
public static final Integer IS_TEXT = 12;
public static final Integer IS_INSTANT = 13;
public static final Integer IS_BINARY = 14;
public static final Integer IS_DATE = 15;
public static final Integer DATE_VALUE = 16;
public static final Integer IS_CURRENCY = 17;
public static final Integer CURRENCY_CODE = 18;
private Map<Integer, String> metaAttributes;
public void clearMetaAttributes() {
@ -58,12 +64,12 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
return this;
}
public DataStructureNode tag(Integer key, String value) {
public DataStructureNode tag(Integer key, Object value) {
if (metaAttributes == null) {
metaAttributes = new HashMap<>();
}
metaAttributes.put(key, value);
metaAttributes.put(key, value.toString());
return this;
}
@ -124,6 +130,7 @@ public abstract class DataStructureNode implements Iterable<DataStructureNode> {
return "("
+ (metaAttributes != null
? metaAttributes.entrySet().stream()
.sorted(Comparator.comparingInt(entry -> entry.getKey()))
.map(e -> e.getValue() != null
? e.getKey() + ":" + e.getValue()
: e.getKey().toString())

View file

@ -6,7 +6,9 @@ import io.xpipe.core.data.type.ValueType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Arrays;
import java.util.Currency;
import java.util.Objects;
public abstract class ValueNode extends DataStructureNode {
@ -25,6 +27,42 @@ public abstract class ValueNode extends DataStructureNode {
return new SimpleValueNode(data);
}
public static ValueNode ofDate(String raw, Instant instant) {
var created = of(raw);
created.tag(IS_DATE);
created.tag(DATE_VALUE, instant.toString());
return created;
}
public static ValueNode ofDecimal(String raw, double decimal) {
return ofDecimal(raw, String.valueOf(decimal));
}
public static ValueNode ofDecimal(String raw, String decimal) {
var created = of(raw);
created.tag(IS_DECIMAL);
created.tag(DECIMAL_VALUE, decimal);
return created;
}
public static ValueNode ofInteger(String raw, long integer) {
return ofInteger(raw, String.valueOf(integer));
}
public static ValueNode ofInteger(String raw, String integer) {
var created = of(raw);
created.tag(IS_INTEGER);
created.tag(INTEGER_VALUE, integer);
return created;
}
public static ValueNode ofCurrency(String raw, String decimal, Currency currency) {
var created = ofDecimal(raw, decimal);
created.tag(IS_CURRENCY);
created.tag(CURRENCY_CODE, currency.getCurrencyCode());
return created;
}
public static ValueNode ofBytes(byte[] data) {
var created = of(data);
created.tag(IS_BINARY);
@ -49,9 +87,15 @@ public abstract class ValueNode extends DataStructureNode {
return created;
}
public static ValueNode ofDecimal(double decimal) {
var created = of(decimal);
created.tag(IS_DECIMAL);
return created;
}
public static ValueNode ofDecimal(BigDecimal decimal) {
var created = of(decimal);
created.tag(IS_FLOATING_POINT);
created.tag(IS_DECIMAL);
return created;
}

View file

@ -3,6 +3,7 @@ package io.xpipe.core.dialog;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import java.util.List;
@ -10,16 +11,18 @@ import java.util.List;
@JsonTypeName("choice")
@EqualsAndHashCode(callSuper = true)
@ToString
@Getter
public class ChoiceElement extends DialogElement {
private final String description;
private final List<Choice> elements;
private final boolean required;
private final boolean quiet;
private int selected;
@JsonCreator
public ChoiceElement(String description, List<Choice> elements, boolean required, int selected) {
public ChoiceElement(String description, List<Choice> elements, boolean required, boolean quiet, int selected) {
if (elements.stream().allMatch(Choice::isDisabled)) {
throw new IllegalArgumentException("All choices are disabled");
}
@ -28,6 +31,7 @@ public class ChoiceElement extends DialogElement {
this.elements = elements;
this.required = required;
this.selected = selected;
this.quiet = quiet;
}
@Override

View file

@ -1,11 +1,11 @@
package io.xpipe.core.dialog;
import io.xpipe.core.charsetter.Charsetter;
import io.xpipe.core.util.SecretValue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@ -22,11 +22,11 @@ import java.util.function.Supplier;
* The evaluation function can be set with {@link #evaluateTo(Supplier)}.
* Alternatively, a dialogue can also copy the evaluation function of another dialogue with {@link #evaluateTo(Dialog)}.
* An evaluation result can also be mapped to another type with {@link #map(Function)}.
* It is also possible to listen for the completion of this dialogue with {@link #onCompletion(Consumer)}.
* It is also possible to listen for the completion of this dialogue with {@link #onCompletion(Charsetter.FailableConsumer)} )}.
*/
public abstract class Dialog {
private final List<Consumer<?>> completion = new ArrayList<>();
private final List<Charsetter.FailableConsumer<?, Exception>> completion = new ArrayList<>();
protected Object eval;
private Supplier<?> evaluation;
@ -65,8 +65,8 @@ public abstract class Dialog {
* @param selected the selected element index
*/
public static Dialog.Choice choice(
String description, List<io.xpipe.core.dialog.Choice> elements, boolean required, int selected) {
Dialog.Choice c = new Dialog.Choice(description, elements, required, selected);
String description, List<io.xpipe.core.dialog.Choice> elements, boolean required, boolean quiet, int selected) {
Dialog.Choice c = new Dialog.Choice(description, elements, required, quiet, selected);
c.evaluateTo(c::getSelected);
return c;
}
@ -77,12 +77,13 @@ public abstract class Dialog {
* @param description the shown question description
* @param toString a function that maps the objects to a string
* @param required signals whether choices required or can be left empty
* @param quiet
* @param def the element which is selected by default
* @param vals the range of possible elements
*/
@SafeVarargs
public static <T> Dialog.Choice choice(
String description, Function<T, String> toString, boolean required, T def, T... vals) {
String description, Function<T, String> toString, boolean required, boolean quiet, T def, T... vals) {
var elements = Arrays.stream(vals)
.map(v -> new io.xpipe.core.dialog.Choice(null, toString.apply(v)))
.toList();
@ -91,7 +92,7 @@ public abstract class Dialog {
throw new IllegalArgumentException("Default value " + def.toString() + " is not in possible values");
}
var c = choice(description, elements, required, index);
var c = choice(description, elements, required, quiet, index);
c.evaluateTo(() -> {
if (c.getSelected() == -1) {
return null;
@ -249,6 +250,9 @@ public abstract class Dialog {
dialog = d.get();
var start = dialog.start();
evaluateTo(dialog);
if (start == null) {
complete();
}
return start;
}
@ -354,7 +358,7 @@ public abstract class Dialog {
boolean required,
int selected,
Function<Integer, Dialog> c) {
var choice = new ChoiceElement(description, elements, required, selected);
var choice = new ChoiceElement(description, elements, required, false, selected);
return new Dialog() {
private Dialog choiceMade;
@ -409,7 +413,7 @@ public abstract class Dialog {
return this;
}
public Dialog onCompletion(Consumer<?> s) {
public Dialog onCompletion(Charsetter.FailableConsumer<?, Exception> s) {
completion.add(s);
return this;
}
@ -419,7 +423,7 @@ public abstract class Dialog {
return this;
}
public Dialog onCompletion(List<Consumer<?>> s) {
public Dialog onCompletion(List<Charsetter.FailableConsumer<?, Exception>> s) {
completion.addAll(s);
return this;
}
@ -430,13 +434,13 @@ public abstract class Dialog {
}
@SuppressWarnings("unchecked")
public <T> void complete() {
public <T> void complete() throws Exception {
if (evaluation != null) {
eval = evaluation.get();
completion.forEach(c -> {
Consumer<T> ct = (Consumer<T>) c;
for (Charsetter.FailableConsumer<?, Exception> c : completion) {
Charsetter.FailableConsumer<T, Exception> ct = (Charsetter.FailableConsumer<T, Exception>) c;
ct.accept((T) eval);
});
}
}
}
@ -459,8 +463,8 @@ public abstract class Dialog {
private final ChoiceElement element;
private Choice(String description, List<io.xpipe.core.dialog.Choice> elements, boolean required, int selected) {
this.element = new ChoiceElement(description, elements, required, selected);
private Choice(String description, List<io.xpipe.core.dialog.Choice> elements, boolean required, boolean quiet, int selected) {
this.element = new ChoiceElement(description, elements, required, quiet, selected);
}
@Override

View file

@ -7,7 +7,6 @@ import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.source.TableReadConnection;
import java.util.OptionalInt;
import java.util.concurrent.atomic.AtomicInteger;
public class BufferedTableReadConnection implements TableReadConnection {
@ -50,18 +49,14 @@ public class BufferedTableReadConnection implements TableReadConnection {
}
@Override
public int withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
AtomicInteger localCounter = new AtomicInteger();
public void withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
TupleNode node;
while (((node = get()) != null)) {
var returned = lineAcceptor.accept(node);
if (!returned) {
break;
}
localCounter.getAndIncrement();
}
return localCounter.get();
}
@Override

View file

@ -6,7 +6,6 @@ import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.source.TableReadConnection;
import java.util.OptionalInt;
import java.util.concurrent.atomic.AtomicInteger;
public class LimitTableReadConnection implements TableReadConnection {
@ -40,20 +39,15 @@ public class LimitTableReadConnection implements TableReadConnection {
}
@Override
public int withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
AtomicInteger localCounter = new AtomicInteger();
public void withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
connection.withRows(node -> {
if (count == maxCount) {
return false;
}
count++;
var returned = lineAcceptor.accept(node);
localCounter.getAndIncrement();
return returned;
return lineAcceptor.accept(node);
});
return localCounter.get();
}
@Override

View file

@ -60,15 +60,14 @@ public class XpbtReadConnection extends StreamReadConnection implements TableRea
}
@Override
public int withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
public void withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
if (empty) {
return 0;
return;
}
var reader = TypedDataStructureNodeReader.of(dataType);
AtomicBoolean quit = new AtomicBoolean(false);
AtomicReference<Exception> exception = new AtomicReference<>();
var counter = 0;
while (!quit.get()) {
var node = parser.parseStructure(inputStream, reader);
if (node == null) {
@ -80,7 +79,6 @@ public class XpbtReadConnection extends StreamReadConnection implements TableRea
if (!lineAcceptor.accept(node.asTuple())) {
quit.set(true);
}
counter++;
} catch (Exception ex) {
quit.set(true);
exception.set(ex);
@ -90,7 +88,6 @@ public class XpbtReadConnection extends StreamReadConnection implements TableRea
if (exception.get() != null) {
throw exception.get();
}
return counter;
}
@Override

View file

@ -58,6 +58,10 @@ public abstract class DataSource<DS extends DataStore> extends JacksonizedValue
store.checkComplete();
}
public void validate() throws Exception {
store.validate();
}
public List<WriteMode> getAvailableWriteModes() {
if (getFlow() != null && !getFlow().hasOutput()) {
return List.of();

View file

@ -25,7 +25,13 @@ public abstract class TableDataSource<DS extends DataStore> extends DataSource<D
@Override
public final DataSourceInfo determineInfo() throws Exception {
if (!getFlow().hasInput()) {
if (!getFlow().hasInput() || !getStore().canOpen()) {
return new DataSourceInfo.Table(null, -1);
}
try {
checkComplete();
} catch (Exception e) {
return new DataSourceInfo.Table(null, -1);
}

View file

@ -37,8 +37,7 @@ public interface TableReadConnection extends DataSourceReadConnection {
}
@Override
public int withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
return 0;
public void withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception {
}
@Override
@ -77,7 +76,7 @@ public interface TableReadConnection extends DataSourceReadConnection {
*
* @return
*/
int withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception;
void withRows(DataStructureNodeAcceptor<TupleNode> lineAcceptor) throws Exception;
/**
* Reads multiple rows in bulk.
@ -119,6 +118,17 @@ public interface TableReadConnection extends DataSourceReadConnection {
var inputType = getDataType();
var tCon = (TableWriteConnection) con;
var mapping = tCon.createMapping(inputType);
return withRows(tCon.writeLinesAcceptor(mapping.orElseThrow()));
var acceptor = tCon.writeLinesAcceptor(mapping.orElseThrow());
AtomicInteger counter = new AtomicInteger();
withRows(acc -> {
if (!acceptor.accept(acc)) {
return false;
}
counter.getAndIncrement();
return true;
});
return counter.get();
}
}

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.charsetter.NewLine;
import io.xpipe.core.util.JacksonizedValue;
import io.xpipe.core.util.SecretValue;
import lombok.Getter;
import java.io.*;
import java.nio.charset.Charset;
@ -69,6 +70,8 @@ public class LocalStore extends JacksonizedValue implements MachineFileStore, St
private final List<SecretValue> input;
private final Integer timeout;
@Getter
private final List<String> command;
private final Charset charset;

View file

@ -5,6 +5,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
@ -125,4 +126,6 @@ public abstract class ProcessControl {
public abstract InputStream getStderr();
public abstract Charset getCharset();
public abstract List<String> getCommand();
}

View file

@ -43,7 +43,7 @@ public interface DataSourceProvider<T extends DataSource<?>> {
return getId() + "." + key;
}
default Region configGui(Property<T> source, boolean all) {
default Region configGui(Property<T> source, boolean preferQuiet) throws Exception {
return null;
}
@ -56,7 +56,7 @@ public interface DataSourceProvider<T extends DataSource<?>> {
}
default String getModuleName() {
var n = getClass().getPackageName();
var n = getClass().getModule().getName();
var i = n.lastIndexOf('.');
return i != -1 ? n.substring(i + 1) : n;
}

View file

@ -33,7 +33,7 @@ public class CharChoiceComp extends Comp<CompStructure<HBox>> {
if (customName != null) {
rangeCopy.put(null, customName);
}
var choice = new ChoiceComp<Character>(value, rangeCopy);
var choice = new ChoiceComp<Character>(value, rangeCopy, false);
var charChoiceR = charChoice.createRegion();
var choiceR = choice.createRegion();
var box = new HBox(charChoiceR, choiceR);

View file

@ -22,15 +22,18 @@ public class ChoiceComp<T> extends Comp<CompStructure<ComboBox<T>>> {
Property<T> value;
ObservableValue<Map<T, ObservableValue<String>>> range;
boolean includeNone;
public ChoiceComp(Property<T> value, Map<T, ObservableValue<String>> range) {
public ChoiceComp(Property<T> value, Map<T, ObservableValue<String>> range, boolean includeNone) {
this.value = value;
this.range = new SimpleObjectProperty<>(range);
this.includeNone = includeNone;
}
public ChoiceComp(Property<T> value, ObservableValue<Map<T, ObservableValue<String>>> range) {
public ChoiceComp(Property<T> value, ObservableValue<Map<T, ObservableValue<String>>> range, boolean includeNone) {
this.value = value;
this.range = PlatformThread.sync(range);
this.includeNone = includeNone;
}
@Override
@ -58,7 +61,7 @@ public class ChoiceComp<T> extends Comp<CompStructure<ComboBox<T>>> {
});
SimpleChangeListener.apply(range, c -> {
var list = FXCollections.observableArrayList(c.keySet());
if (!list.contains(null)) {
if (!list.contains(null) && includeNone) {
list.add(null);
}
cb.setItems(list);

View file

@ -31,7 +31,7 @@ public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
public CompStructure<FlowPane> createBase() {
var flow = new FlowPane(Orientation.HORIZONTAL);
flow.setAlignment(Pos.CENTER);
flow.setHgap(7);
flow.setHgap(14);
flow.setVgap(7);
var nameRegions = new ArrayList<Region>();

View file

@ -0,0 +1,31 @@
package io.xpipe.extension.util;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.ValueNode;
import java.util.Currency;
import java.util.Optional;
public class DataTypeParser {
public static Optional<ValueNode> parseMonetary(String val) {
for (Currency availableCurrency : Currency.getAvailableCurrencies()) {
if (val.contains(availableCurrency.getSymbol())) {
String newStr = DataTypeParserInternal.cleanseNumberString(val);
var node = DataTypeParserInternal.parseDecimalFromCleansed(newStr);
if (node.isEmpty()) {
continue;
}
return Optional.of(ValueNode.ofCurrency(
val, node.get().getMetaAttribute(DataStructureNode.DECIMAL_VALUE), availableCurrency));
}
}
return Optional.empty();
}
public static Optional<ValueNode> parseNumber(String val) {
var cleansed = DataTypeParserInternal.cleanseNumberString(val);
return DataTypeParserInternal.parseNumberFromCleansed(cleansed);
}
}

View file

@ -0,0 +1,104 @@
package io.xpipe.extension.util;
import io.xpipe.core.data.node.ValueNode;
import org.apache.commons.lang3.math.NumberUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;
import java.util.regex.Pattern;
public class DataTypeParserInternal {
private static final Pattern DECIMAL_IS_INTEGER = Pattern.compile("^([-\\d]+?)(\\.0*)?$");
private static final Pattern TRAILING_ZEROS = Pattern.compile("^(.+?\\.\\d*)0+$");
private static final Pattern LONG = Pattern.compile("^[+-]?[0-9]+$");
private static final Pattern DECIMAL = Pattern.compile("^[+-]?([0-9]+)(\\.([0-9]+))?$");
static String cleanseNumberString(String value) {
value = value.replaceAll("[^-\\d.]+", "");
return value;
}
static Optional<ValueNode> parseDecimalFromCleansed(String val) {
// Normal decimal
var simpleDecimal = parseSimpleDecimalValue(val);
if (simpleDecimal.isPresent()) {
return Optional.of(ValueNode.ofDecimal(val, simpleDecimal.get()));
}
// Specially formatted number, must be in range of double
if (NumberUtils.isCreatable(val)) {
var number = NumberUtils.createNumber(val);
if (number instanceof Float || number instanceof Double) {
return Optional.of(ValueNode.ofDecimal(val, number.doubleValue()));
}
}
// Big decimal value
try {
var bigDecimal = new BigDecimal(val);
return Optional.of(ValueNode.ofDecimal(bigDecimal));
} catch (Exception e) {
}
return Optional.empty();
}
private static Optional<String> parseSimpleDecimalValue(String val) {
var decimalMatcher = DECIMAL.matcher(val);
if (decimalMatcher.matches()) {
var integerMatcher = DECIMAL_IS_INTEGER.matcher(val);
if (integerMatcher.matches()) {
return Optional.of(integerMatcher.group(1));
}
var trailingRemoved = TRAILING_ZEROS.matcher(val);
if (trailingRemoved.matches()) {
return Optional.of(trailingRemoved.group(1));
}
return Optional.of(val);
}
return Optional.empty();
}
static Optional<ValueNode> parseNumberFromCleansed(String val) {
// Simple integer
if (LONG.matcher(val).matches()) {
return Optional.of(ValueNode.ofInteger(val, val));
}
// Simple decimal
var simpleDecimal = parseSimpleDecimalValue(val);
if (simpleDecimal.isPresent()) {
return Optional.of(ValueNode.ofDecimal(val, simpleDecimal.get()));
}
// Specially formatted number, must be in range of double or long
if (NumberUtils.isCreatable(val)) {
var number = NumberUtils.createNumber(val);
if (number instanceof Float || number instanceof Double) {
return Optional.of(ValueNode.ofDecimal(val, number.doubleValue()));
} else {
return Optional.of(ValueNode.ofInteger(val, number.longValue()));
}
}
// Big integer value
try {
var bigInteger = new BigInteger(val);
return Optional.of(ValueNode.ofInteger(bigInteger));
} catch (Exception e) {
}
// Big decimal value
try {
var bigDecimal = new BigDecimal(val);
return Optional.of(ValueNode.ofDecimal(bigDecimal));
} catch (Exception e) {
}
return Optional.empty();
}
}

View file

@ -42,7 +42,7 @@ public class DialogHelper {
}
public static Dialog dataStoreFlowQuery(DataFlow flow, DataFlow[] available) {
return Dialog.choice("Flow", (DataFlow o) -> o.getDisplayName(), true, flow, available);
return Dialog.choice("Flow", (DataFlow o) -> o.getDisplayName(), true, false, flow, available);
}
public static Dialog shellQuery(String displayName, DataStore store) {
@ -66,16 +66,20 @@ public class DialogHelper {
});
}
public static Dialog charsetQuery(StreamCharset c, boolean all) {
return Dialog.query("Charset", false, true, c != null && !all, c, QueryConverter.CHARSET);
public static Dialog charsetQuery(StreamCharset c, boolean preferQuiet) {
return Dialog.query("Charset", false, true, c != null && preferQuiet, c, QueryConverter.CHARSET);
}
public static Dialog newLineQuery(NewLine n, boolean all) {
return Dialog.query("Newline", false, true, n != null && !all, n, QueryConverter.NEW_LINE);
public static Dialog newLineQuery(NewLine n, boolean preferQuiet) {
return Dialog.query("Newline", false, true, n != null && preferQuiet, n, QueryConverter.NEW_LINE);
}
public static <T> Dialog query(String desc, T value, boolean required, QueryConverter<T> c, boolean all) {
return Dialog.query(desc, false, required, value != null && !all, value, c);
public static <T> Dialog query(String desc, T value, boolean required, QueryConverter<T> c, boolean preferQuiet) {
return Dialog.query(desc, false, required, value != null && preferQuiet, value, c);
}
public static Dialog booleanChoice(String desc, boolean value, boolean preferQuiet) {
return Dialog.choice(desc, val -> val.toString(), true, preferQuiet, value, Boolean.TRUE, Boolean.FALSE);
}
public static Dialog fileQuery(String name) {

View file

@ -69,7 +69,7 @@ public class DynamicOptionsBuilder {
for (var e : NewLine.values()) {
map.put(e, I18n.observable("extension." + e.getId()));
}
var comp = new ChoiceComp<>(prop, map);
var comp = new ChoiceComp<>(prop, map, false);
entries.add(new DynamicOptionsComp.Entry(I18n.observable("extension.newLine"), comp));
props.add(prop);
return this;
@ -94,6 +94,14 @@ public class DynamicOptionsBuilder {
return this;
}
public DynamicOptionsBuilder addToggle(String nameKey,
Property<Boolean> prop) {
var comp = new ToggleGroupComp<>(prop, new SimpleObjectProperty<>(Map.of(Boolean.TRUE, I18n.observable("extension.yes"), Boolean.FALSE, I18n.observable("extension.no"))));
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
props.add(prop);
return this;
}
public <V> DynamicOptionsBuilder addToggle(
Property<V> prop, ObservableValue<String> name, Map<V, ObservableValue<String>> names) {
var comp = new ToggleGroupComp<>(prop, new SimpleObjectProperty<>(names));
@ -103,16 +111,18 @@ public class DynamicOptionsBuilder {
}
public <V> DynamicOptionsBuilder addChoice(
Property<V> prop, ObservableValue<String> name, Map<V, ObservableValue<String>> names) {
var comp = new ChoiceComp<>(prop, names);
Property<V> prop, ObservableValue<String> name, Map<V, ObservableValue<String>> names, boolean includeNone
) {
var comp = new ChoiceComp<>(prop, names, includeNone);
entries.add(new DynamicOptionsComp.Entry(name, comp));
props.add(prop);
return this;
}
public <V> DynamicOptionsBuilder addChoice(
Property<V> prop, ObservableValue<String> name, ObservableValue<Map<V, ObservableValue<String>>> names) {
var comp = new ChoiceComp<>(prop, names);
Property<V> prop, ObservableValue<String> name, ObservableValue<Map<V, ObservableValue<String>>> names, boolean includeNone
) {
var comp = new ChoiceComp<>(prop, names, includeNone);
entries.add(new DynamicOptionsComp.Entry(name, comp));
props.add(prop);
return this;

View file

@ -6,6 +6,7 @@ import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.FileStore;
import io.xpipe.core.util.JacksonMapper;
import io.xpipe.extension.XPipeServiceProviders;
import lombok.SneakyThrows;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@ -13,12 +14,13 @@ import java.nio.file.Path;
public class ExtensionTest {
@SneakyThrows
public static DataStore getResource(String name) {
var url = ExtensionTest.class.getClassLoader().getResource(name);
if (url == null) {
throw new IllegalArgumentException(String.format("File %s does not exist", name));
}
var file = url.getFile().substring(1);
var file = Path.of(url.toURI()).toString();
return FileStore.local(Path.of(file));
}

View file

@ -33,7 +33,7 @@ public class TypeConverter {
if (NumberUtils.isCreatable(string)) {
var number = NumberUtils.createNumber(string);
if (number instanceof Float || number instanceof Double) {
node.tag(DataStructureNode.IS_FLOATING_POINT);
node.tag(DataStructureNode.IS_DECIMAL);
} else {
node.tag(DataStructureNode.IS_INTEGER);
}
@ -45,8 +45,8 @@ public class TypeConverter {
return BigDecimal.ZERO;
}
if (node.hasMetaAttribute(DataStructureNode.FLOATING_POINT_VALUE)) {
return new BigDecimal(node.getMetaAttribute(DataStructureNode.FLOATING_POINT_VALUE));
if (node.hasMetaAttribute(DataStructureNode.DECIMAL_VALUE)) {
return new BigDecimal(node.getMetaAttribute(DataStructureNode.DECIMAL_VALUE));
}
var parsedDecimal = parseDecimal(node.asString());
@ -101,8 +101,8 @@ public class TypeConverter {
return parsedInteger;
}
if (node.hasMetaAttribute(DataStructureNode.FLOATING_POINT_VALUE)) {
return new BigDecimal(node.getMetaAttribute(DataStructureNode.FLOATING_POINT_VALUE)).toBigInteger();
if (node.hasMetaAttribute(DataStructureNode.DECIMAL_VALUE)) {
return new BigDecimal(node.getMetaAttribute(DataStructureNode.DECIMAL_VALUE)).toBigInteger();
}
var parsedDecimal = parseDecimal(node.asString());

View file

@ -24,4 +24,6 @@ append=Append
prepend=Prepend
replaceDescription=Replaces all content
appendDescription=Appends the new content to the existing content
prependDescription=Prepends the new content to the existing content
prependDescription=Prepends the new content to the existing content
yes=Yes
no=No