mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-09-30 00:56:56 +13:00
Various small fixes
This commit is contained in:
parent
71b5d2d716
commit
9a2a1a8745
18 changed files with 415 additions and 137 deletions
|
@ -42,10 +42,8 @@ public abstract class DataSourceImpl implements DataSource {
|
|||
case RAW -> {
|
||||
yield new DataRawImpl(res.getId(), config, res.getInternalSource());
|
||||
}
|
||||
case COLLECTION -> throw new UnsupportedOperationException(
|
||||
"Unimplemented case: " + res.getType());
|
||||
default -> throw new IllegalArgumentException(
|
||||
"Unexpected value: " + res.getType());
|
||||
case COLLECTION -> throw new UnsupportedOperationException("Unimplemented case: " + res.getType());
|
||||
default -> throw new IllegalArgumentException("Unexpected value: " + res.getType());
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -64,17 +62,18 @@ public abstract class DataSourceImpl implements DataSource {
|
|||
|
||||
public static DataSource create(DataSourceId id, String type, DataStore store) {
|
||||
if (store instanceof StreamDataStore s && s.isContentExclusivelyAccessible()) {
|
||||
var res = XPipeApiConnection.execute(con -> {
|
||||
var req = StoreStreamExchange.Request.builder().build();
|
||||
StoreStreamExchange.Response r = con.performOutputExchange(req, out -> {
|
||||
store = XPipeApiConnection.execute(con -> {
|
||||
var internal = con.createInternalStreamStore();
|
||||
var req = WriteStreamExchange.Request.builder()
|
||||
.name(internal.getUuid().toString())
|
||||
.build();
|
||||
con.performOutputExchange(req, out -> {
|
||||
try (InputStream inputStream = s.openInput()) {
|
||||
inputStream.transferTo(out);
|
||||
}
|
||||
});
|
||||
return r;
|
||||
return internal;
|
||||
});
|
||||
|
||||
store = res.getStore();
|
||||
}
|
||||
|
||||
var startReq = ReadExchange.Request.builder()
|
||||
|
@ -96,14 +95,17 @@ public abstract class DataSourceImpl implements DataSource {
|
|||
}
|
||||
|
||||
public static DataSource create(DataSourceId id, String type, InputStream in) {
|
||||
var res = XPipeApiConnection.execute(con -> {
|
||||
var req = StoreStreamExchange.Request.builder().build();
|
||||
StoreStreamExchange.Response r = con.performOutputExchange(req, out -> in.transferTo(out));
|
||||
return r;
|
||||
var store = XPipeApiConnection.execute(con -> {
|
||||
var internal = con.createInternalStreamStore();
|
||||
var req = WriteStreamExchange.Request.builder()
|
||||
.name(internal.getUuid().toString())
|
||||
.build();
|
||||
con.performOutputExchange(req, out -> {
|
||||
in.transferTo(out);
|
||||
});
|
||||
return internal;
|
||||
});
|
||||
|
||||
var store = res.getStore();
|
||||
|
||||
var startReq = ReadExchange.Request.builder()
|
||||
.provider(type)
|
||||
.store(store)
|
||||
|
|
|
@ -7,12 +7,15 @@ import io.xpipe.api.connector.XPipeApiConnection;
|
|||
import io.xpipe.api.util.TypeDescriptor;
|
||||
import io.xpipe.beacon.BeaconException;
|
||||
import io.xpipe.beacon.exchange.ReadExchange;
|
||||
import io.xpipe.beacon.exchange.StoreStreamExchange;
|
||||
import io.xpipe.beacon.exchange.WriteStreamExchange;
|
||||
import io.xpipe.beacon.exchange.cli.StoreAddExchange;
|
||||
import io.xpipe.beacon.util.QuietDialogHandler;
|
||||
import io.xpipe.core.data.node.DataStructureNode;
|
||||
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
|
||||
import io.xpipe.core.data.node.TupleNode;
|
||||
import io.xpipe.core.data.type.TupleType;
|
||||
import io.xpipe.core.data.typed.TypedDataStreamWriter;
|
||||
import io.xpipe.core.impl.InternalStreamStore;
|
||||
import io.xpipe.core.source.DataSourceId;
|
||||
import io.xpipe.core.source.DataSourceReference;
|
||||
|
||||
|
@ -25,13 +28,20 @@ public class DataTableAccumulatorImpl implements DataTableAccumulator {
|
|||
private final XPipeApiConnection connection;
|
||||
private final TupleType type;
|
||||
private int rows;
|
||||
private InternalStreamStore store;
|
||||
private TupleType writtenDescriptor;
|
||||
private OutputStream bodyOutput;
|
||||
|
||||
public DataTableAccumulatorImpl(TupleType type) {
|
||||
this.type = type;
|
||||
connection = XPipeApiConnection.open();
|
||||
connection.sendRequest(StoreStreamExchange.Request.builder().build());
|
||||
|
||||
store = new InternalStreamStore();
|
||||
var addReq = StoreAddExchange.Request.builder().storeInput(store).name(store.getUuid().toString()).build();
|
||||
StoreAddExchange.Response addRes = connection.performSimpleExchange(addReq);
|
||||
QuietDialogHandler.handle(addRes.getConfig(), connection);
|
||||
|
||||
connection.sendRequest(WriteStreamExchange.Request.builder().name(store.getUuid().toString()).build());
|
||||
bodyOutput = connection.sendBody();
|
||||
}
|
||||
|
||||
|
@ -43,12 +53,12 @@ public class DataTableAccumulatorImpl implements DataTableAccumulator {
|
|||
throw new BeaconException(e);
|
||||
}
|
||||
|
||||
StoreStreamExchange.Response res = connection.receiveResponse();
|
||||
WriteStreamExchange.Response res = connection.receiveResponse();
|
||||
connection.close();
|
||||
|
||||
var req = ReadExchange.Request.builder()
|
||||
.target(id)
|
||||
.store(res.getStore())
|
||||
.store(store)
|
||||
.provider("xpbt")
|
||||
.configureAll(false)
|
||||
.build();
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package io.xpipe.beacon;
|
||||
|
||||
import io.xpipe.beacon.exchange.cli.StoreAddExchange;
|
||||
import io.xpipe.beacon.util.QuietDialogHandler;
|
||||
import io.xpipe.core.impl.InternalStreamStore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -169,6 +173,14 @@ public abstract class BeaconConnection implements AutoCloseable {
|
|||
}
|
||||
}
|
||||
|
||||
public InternalStreamStore createInternalStreamStore() {
|
||||
var store = new InternalStreamStore();
|
||||
var addReq = StoreAddExchange.Request.builder().storeInput(store).name(store.getUuid().toString()).build();
|
||||
StoreAddExchange.Response addRes = performSimpleExchange(addReq);
|
||||
QuietDialogHandler.handle(addRes.getConfig(), this);
|
||||
return store;
|
||||
}
|
||||
|
||||
private BeaconException unwrapException(Exception exception) {
|
||||
if (exception instanceof ServerException s) {
|
||||
return new BeaconException("An internal server error occurred", s);
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package io.xpipe.beacon.exchange;
|
||||
|
||||
import io.xpipe.beacon.RequestMessage;
|
||||
import io.xpipe.beacon.ResponseMessage;
|
||||
import io.xpipe.core.impl.FileStore;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
/**
|
||||
* Stores a stream of data in a storage.
|
||||
*/
|
||||
public class StoreStreamExchange implements MessageExchange {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "storeStream";
|
||||
}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Request implements RequestMessage {}
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
@Value
|
||||
public static class Response implements ResponseMessage {
|
||||
FileStore store;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package io.xpipe.beacon.util;
|
||||
|
||||
import io.xpipe.beacon.BeaconConnection;
|
||||
import io.xpipe.beacon.ClientException;
|
||||
import io.xpipe.beacon.BeaconException;
|
||||
import io.xpipe.beacon.exchange.cli.DialogExchange;
|
||||
import io.xpipe.core.dialog.BaseQueryElement;
|
||||
import io.xpipe.core.dialog.ChoiceElement;
|
||||
|
@ -13,7 +13,7 @@ import java.util.UUID;
|
|||
|
||||
public class QuietDialogHandler {
|
||||
|
||||
public static void handle(DialogReference ref, BeaconConnection connection) throws ClientException {
|
||||
public static void handle(DialogReference ref, BeaconConnection connection) {
|
||||
new QuietDialogHandler(ref, connection, Map.of()).handle();
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ public class QuietDialogHandler {
|
|||
this.overrides = overrides;
|
||||
}
|
||||
|
||||
public void handle() throws ClientException {
|
||||
public void handle() {
|
||||
String response = null;
|
||||
|
||||
if (element instanceof ChoiceElement c) {
|
||||
|
@ -45,7 +45,7 @@ public class QuietDialogHandler {
|
|||
.value(response)
|
||||
.build());
|
||||
if (res.getElement() != null && element.equals(res.getElement())) {
|
||||
throw new ClientException(
|
||||
throw new BeaconException(
|
||||
"Invalid value for key " + res.getElement().toDisplayString());
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ module io.xpipe.beacon {
|
|||
ListStoresExchange,
|
||||
DialogExchange,
|
||||
QueryDataSourceExchange,
|
||||
StoreStreamExchange,
|
||||
EditExchange,
|
||||
RemoveEntryExchange,
|
||||
RemoveCollectionExchange,
|
||||
|
|
|
@ -1,49 +1,111 @@
|
|||
package io.xpipe.core.charsetter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import io.xpipe.core.util.Identifiers;
|
||||
import lombok.Value;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Value
|
||||
public class StreamCharset {
|
||||
|
||||
public static final StreamCharset UTF8 = new StreamCharset(StandardCharsets.UTF_8, null);
|
||||
public static final StreamCharset UTF8_BOM =
|
||||
new StreamCharset(StandardCharsets.UTF_8, new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
|
||||
public static final StreamCharset UTF16_BE = new StreamCharset(StandardCharsets.UTF_16BE, null);
|
||||
public static final StreamCharset UTF16_BE_BOM =
|
||||
new StreamCharset(StandardCharsets.UTF_16BE, new byte[] {(byte) 0xFE, (byte) 0xFF});
|
||||
public static final StreamCharset UTF16_LE = new StreamCharset(StandardCharsets.UTF_16LE, null);
|
||||
public static final StreamCharset UTF16_LE_BOM =
|
||||
new StreamCharset(StandardCharsets.UTF_16LE, new byte[] {(byte) 0xFF, (byte) 0xFE});
|
||||
public static final StreamCharset UTF8 =
|
||||
new StreamCharset(StandardCharsets.UTF_8, null, Identifiers.get("utf", "8"));
|
||||
|
||||
public static final StreamCharset UTF32_LE = new StreamCharset(Charset.forName("utf-32le"), null);
|
||||
public static final StreamCharset UTF32_LE_BOM =
|
||||
new StreamCharset(Charset.forName("utf-32le"), new byte[] {0x00, 0x00, (byte) 0xFE, (byte) 0xFF});
|
||||
public static final StreamCharset UTF32_BE = new StreamCharset(Charset.forName("utf-32be"), null);
|
||||
public static final StreamCharset UTF32_BE_BOM =
|
||||
new StreamCharset(Charset.forName("utf-32be"), new byte[] {(byte) 0xFF, (byte) 0xFE, 0x00, 0x00, });
|
||||
public static final StreamCharset UTF8_BOM = new StreamCharset(
|
||||
StandardCharsets.UTF_8,
|
||||
new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF},
|
||||
Identifiers.get("utf", "8", "bom"));
|
||||
|
||||
// ======
|
||||
// UTF-16
|
||||
// ======
|
||||
|
||||
public static final StreamCharset UTF16_BE =
|
||||
new StreamCharset(StandardCharsets.UTF_16BE, null, Identifiers.get("utf", "16", "be"));
|
||||
|
||||
public static final StreamCharset UTF16_BE_BOM = new StreamCharset(
|
||||
StandardCharsets.UTF_16BE,
|
||||
new byte[] {(byte) 0xFE, (byte) 0xFF},
|
||||
Identifiers.get("utf", "16", "be", "bom"));
|
||||
|
||||
public static final StreamCharset UTF16_LE =
|
||||
new StreamCharset(StandardCharsets.UTF_16LE, null, Identifiers.get("utf", "16", "le"));
|
||||
|
||||
public static final StreamCharset UTF16_LE_BOM = new StreamCharset(
|
||||
StandardCharsets.UTF_16LE,
|
||||
new byte[] {(byte) 0xFF, (byte) 0xFE},
|
||||
Identifiers.get("utf", "16", "le", "bom"));
|
||||
|
||||
public static final StreamCharset UTF16 =
|
||||
new StreamCharset(StandardCharsets.UTF_16, null, Identifiers.get("utf", "16"));
|
||||
|
||||
public static final StreamCharset UTF16_BOM = new StreamCharset(
|
||||
StandardCharsets.UTF_16,
|
||||
ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)
|
||||
? UTF16_BE_BOM.getByteOrderMark()
|
||||
: UTF16_LE_BOM.getByteOrderMark(),
|
||||
Identifiers.get("utf", "16", "bom"));
|
||||
|
||||
// ======
|
||||
// UTF-32
|
||||
// ======
|
||||
|
||||
public static final StreamCharset UTF32_LE =
|
||||
new StreamCharset(Charset.forName("utf-32le"), null, Identifiers.get("utf", "32", "le"));
|
||||
|
||||
public static final StreamCharset UTF32_LE_BOM = new StreamCharset(
|
||||
Charset.forName("utf-32le"),
|
||||
new byte[] {0x00, 0x00, (byte) 0xFE, (byte) 0xFF},
|
||||
Identifiers.get("utf", "32", "le", "bom"));
|
||||
|
||||
public static final StreamCharset UTF32_BE =
|
||||
new StreamCharset(Charset.forName("utf-32be"), null, Identifiers.get("utf", "32", "be"));
|
||||
|
||||
public static final StreamCharset UTF32_BE_BOM = new StreamCharset(
|
||||
Charset.forName("utf-32be"),
|
||||
new byte[] {
|
||||
(byte) 0xFF, (byte) 0xFE, 0x00, 0x00,
|
||||
},
|
||||
Identifiers.get("utf", "32", "be", "bom"));
|
||||
|
||||
public static final List<StreamCharset> COMMON = List.of(
|
||||
UTF8,
|
||||
UTF8_BOM,
|
||||
UTF16_BE,
|
||||
UTF16_BE_BOM,
|
||||
UTF16_LE,
|
||||
UTF16_LE_BOM,
|
||||
new StreamCharset(StandardCharsets.US_ASCII, null),
|
||||
new StreamCharset(StandardCharsets.ISO_8859_1, null),
|
||||
new StreamCharset(Charset.forName("Windows-1251"), null),
|
||||
new StreamCharset(Charset.forName("Windows-1252"), null));
|
||||
private static final List<StreamCharset> RARE_KNOWN = List.of(UTF32_LE, UTF32_LE_BOM, UTF32_BE, UTF32_BE_BOM);
|
||||
UTF16,
|
||||
UTF16_BOM,
|
||||
new StreamCharset(
|
||||
StandardCharsets.US_ASCII,
|
||||
null,
|
||||
Identifiers.join(Identifiers.get("ascii"), Identifiers.get("us", "ascii"))),
|
||||
new StreamCharset(
|
||||
StandardCharsets.ISO_8859_1,
|
||||
null,
|
||||
Identifiers.join(
|
||||
Identifiers.get("iso", "8859"),
|
||||
Identifiers.get("iso", "8859", "1"),
|
||||
Identifiers.get("8859"),
|
||||
Identifiers.get("8859", "1"))),
|
||||
new StreamCharset(
|
||||
Charset.forName("Windows-1251"),
|
||||
null,
|
||||
Identifiers.join(Identifiers.get("windows", "1251"), Identifiers.get("1251"))),
|
||||
new StreamCharset(
|
||||
Charset.forName("Windows-1252"),
|
||||
null,
|
||||
Identifiers.join(Identifiers.get("windows", "1252"), Identifiers.get("1252"))));
|
||||
|
||||
private static final List<StreamCharset> RARE_NAMED =
|
||||
List.of(UTF16_LE, UTF16_LE_BOM, UTF16_BE, UTF16_BE_BOM, UTF32_LE, UTF32_LE_BOM, UTF32_BE, UTF32_BE_BOM);
|
||||
|
||||
public static final List<StreamCharset> RARE = Stream.concat(
|
||||
RARE_KNOWN.stream(),
|
||||
RARE_NAMED.stream(),
|
||||
Charset.availableCharsets().values().stream()
|
||||
.filter(charset -> !charset.equals(StandardCharsets.UTF_16)
|
||||
&& !charset.equals(Charset.forName("utf-32"))
|
||||
|
@ -52,31 +114,59 @@ public class StreamCharset {
|
|||
&& !charset.displayName().endsWith("-BOM")
|
||||
&& COMMON.stream()
|
||||
.noneMatch(c -> c.getCharset().equals(charset))
|
||||
&& RARE_KNOWN.stream()
|
||||
&& RARE_NAMED.stream()
|
||||
.noneMatch(c -> c.getCharset().equals(charset)))
|
||||
.map(charset -> new StreamCharset(charset, null)))
|
||||
.map(charset -> new StreamCharset(
|
||||
charset,
|
||||
null,
|
||||
Identifiers.get(charset.name().split("-")))))
|
||||
.toList();
|
||||
|
||||
public static final List<StreamCharset> ALL = Stream.concat(COMMON.stream(), RARE.stream()).toList();
|
||||
|
||||
|
||||
|
||||
Charset charset;
|
||||
byte[] byteOrderMark;
|
||||
List<String> names;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof StreamCharset that)) {
|
||||
return false;
|
||||
}
|
||||
return charset.equals(that.charset) && Arrays.equals(byteOrderMark, that.byteOrderMark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Objects.hash(charset);
|
||||
result = 31 * result + Arrays.hashCode(byteOrderMark);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static StreamCharset get(Charset charset, boolean byteOrderMark) {
|
||||
return Stream.concat(COMMON.stream(), RARE.stream())
|
||||
return ALL.stream()
|
||||
.filter(streamCharset ->
|
||||
streamCharset.getCharset().equals(charset) && streamCharset.hasByteOrderMark() == byteOrderMark)
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static StreamCharset get(String s) {
|
||||
var byteOrderMark = s.endsWith("-bom");
|
||||
var charset = Charset.forName(s.substring(0, s.length() - (byteOrderMark ? 4 : 0)));
|
||||
return StreamCharset.get(charset, byteOrderMark);
|
||||
var found = ALL.stream().filter(streamCharset -> streamCharset.getNames().contains(s.toLowerCase(Locale.ROOT))).findFirst();
|
||||
if (found.isEmpty()) {
|
||||
throw new IllegalArgumentException("Unknown charset name: " + s);
|
||||
}
|
||||
|
||||
return found.get();
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String toString() {
|
||||
return getCharset().name().toLowerCase(Locale.ROOT) + (hasByteOrderMark() ? "-bom" : "");
|
||||
return getNames().get(0);
|
||||
}
|
||||
|
||||
public boolean hasByteOrderMark() {
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package io.xpipe.core.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.core.store.DataFlow;
|
||||
import io.xpipe.core.store.StreamDataStore;
|
||||
import io.xpipe.core.util.DataStateProvider;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@JsonTypeName("internalStream")
|
||||
@SuperBuilder
|
||||
@Jacksonized
|
||||
@Getter
|
||||
public class InternalStreamStore extends JacksonizedValue implements StreamDataStore {
|
||||
|
||||
private final UUID uuid;
|
||||
|
||||
public InternalStreamStore() {
|
||||
this.uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlow getFlow() {
|
||||
return DataFlow.INPUT_OUTPUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> determineDefaultName() {
|
||||
return Optional.of(uuid.toString());
|
||||
}
|
||||
|
||||
private Path getFile() {
|
||||
return DataStateProvider.get().getInternalStreamStore(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Instant> determineLastModified() throws IOException {
|
||||
return Optional.of(Files.getLastModifiedTime(getFile()).toInstant());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInput() throws Exception {
|
||||
return Files.newInputStream(getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutput() throws Exception {
|
||||
return Files.newOutputStream(getFile());
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import io.xpipe.core.impl.StdinDataStore;
|
|||
import io.xpipe.core.impl.StdoutDataStore;
|
||||
import io.xpipe.core.source.DataSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -101,7 +102,7 @@ public interface DataStore {
|
|||
/**
|
||||
* Determines the last modified of this data store if this data store supports it.
|
||||
*/
|
||||
default Optional<Instant> determineLastModified() {
|
||||
default Optional<Instant> determineLastModified() throws IOException {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ package io.xpipe.core.util;
|
|||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class DataStateProvider {
|
||||
|
@ -22,4 +24,6 @@ public abstract class DataStateProvider {
|
|||
public abstract void putState(DataStore store, String key, Object value);
|
||||
|
||||
public abstract <T> T getState(DataStore store, String key, Class<T> c, Supplier<T> def);
|
||||
|
||||
public abstract Path getInternalStreamStore(UUID id);
|
||||
}
|
||||
|
|
25
core/src/main/java/io/xpipe/core/util/Identifiers.java
Normal file
25
core/src/main/java/io/xpipe/core/util/Identifiers.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package io.xpipe.core.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class Identifiers {
|
||||
|
||||
@SafeVarargs
|
||||
public static List<String> join(List<String>... s) {
|
||||
return Arrays.stream(s).flatMap(Collection::stream).toList();
|
||||
}
|
||||
|
||||
public static List<String> get(String... s) {
|
||||
return nameAlternatives(Arrays.asList(s));
|
||||
}
|
||||
|
||||
private static List<String> nameAlternatives(List<String> split) {
|
||||
return List.of(
|
||||
String.join("", split),
|
||||
String.join(" ", split),
|
||||
String.join("_", split),
|
||||
String.join("-", split));
|
||||
}
|
||||
}
|
|
@ -8,11 +8,16 @@ import java.time.Instant;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class TrackEvent {
|
||||
|
||||
public static TrackEventBuilder storage() {
|
||||
return TrackEvent.builder().category("storage");
|
||||
}
|
||||
|
||||
private final Thread thread = Thread.currentThread();
|
||||
private final Instant instant = Instant.now();
|
||||
private String type;
|
||||
|
@ -107,10 +112,17 @@ public class TrackEvent {
|
|||
if (tags.size() > 0) {
|
||||
s.append(" {\n");
|
||||
for (var e : tags.entrySet()) {
|
||||
var value = e.toString().contains("\n")
|
||||
? "\n"
|
||||
+ (e.toString()
|
||||
.lines()
|
||||
.map(line -> " | " + line)
|
||||
.collect(Collectors.joining("\n")))
|
||||
: e.toString();
|
||||
s.append(" ")
|
||||
.append(e.getKey())
|
||||
.append("=")
|
||||
.append(e.getValue())
|
||||
.append(value)
|
||||
.append("\n");
|
||||
}
|
||||
s.append("}");
|
||||
|
@ -120,6 +132,11 @@ public class TrackEvent {
|
|||
|
||||
public static class TrackEventBuilder {
|
||||
|
||||
public TrackEventBuilder trace() {
|
||||
this.type("trace");
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackEventBuilder windowCategory() {
|
||||
this.category("window");
|
||||
return this;
|
||||
|
|
|
@ -47,6 +47,10 @@ public abstract class Comp<S extends CompStructure<?>> {
|
|||
return (T) this;
|
||||
}
|
||||
|
||||
public Comp<S> visible(ObservableValue<Boolean> o) {
|
||||
return apply(struc -> struc.get().visibleProperty().bind(o));
|
||||
}
|
||||
|
||||
public Comp<S> disable(ObservableValue<Boolean> o) {
|
||||
return apply(struc -> struc.get().disableProperty().bind(o));
|
||||
}
|
||||
|
|
|
@ -3,21 +3,19 @@ package io.xpipe.extension.fxcomps.impl;
|
|||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.SimpleCompStructure;
|
||||
import io.xpipe.extension.fxcomps.util.PlatformThread;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.FlowPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
|
||||
public class DynamicOptionsComp extends Comp<CompStructure<Pane>> {
|
||||
|
||||
private final List<Entry> entries;
|
||||
private final boolean wrap;
|
||||
|
@ -27,12 +25,28 @@ public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
|
|||
this.wrap = wrap;
|
||||
}
|
||||
|
||||
public Entry queryEntry(String key) {
|
||||
return entries.stream()
|
||||
.filter(entry -> entry.key != null && entry.key.equals(key))
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<FlowPane> createBase() {
|
||||
var flow = new FlowPane(Orientation.HORIZONTAL);
|
||||
flow.setAlignment(Pos.CENTER);
|
||||
flow.setHgap(14);
|
||||
flow.setVgap(7);
|
||||
public CompStructure<Pane> createBase() {
|
||||
Pane pane;
|
||||
if (wrap) {
|
||||
var content = new FlowPane(Orientation.HORIZONTAL);
|
||||
content.setAlignment(Pos.CENTER);
|
||||
content.setHgap(14);
|
||||
content.setVgap(7);
|
||||
pane = content;
|
||||
} else {
|
||||
var content = new VBox();
|
||||
content.setAlignment(Pos.CENTER);
|
||||
content.setSpacing(7);
|
||||
pane = content;
|
||||
}
|
||||
|
||||
var nameRegions = new ArrayList<Region>();
|
||||
var compRegions = new ArrayList<Region>();
|
||||
|
@ -41,30 +55,38 @@ public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
|
|||
var line = new HBox();
|
||||
line.setFillHeight(true);
|
||||
if (!wrap) {
|
||||
line.prefWidthProperty().bind(flow.widthProperty());
|
||||
line.prefWidthProperty().bind(pane.widthProperty());
|
||||
}
|
||||
line.setSpacing(8);
|
||||
|
||||
Region compRegion = null;
|
||||
if (entry.comp() != null) {
|
||||
compRegion = entry.comp().createRegion();
|
||||
}
|
||||
|
||||
if (entry.name() != null) {
|
||||
var name = new Label();
|
||||
name.textProperty().bind(entry.name());
|
||||
name.prefHeightProperty().bind(line.heightProperty());
|
||||
name.setMinWidth(Region.USE_PREF_SIZE);
|
||||
name.setAlignment(Pos.CENTER_LEFT);
|
||||
if (compRegion != null) {
|
||||
name.visibleProperty().bind(PlatformThread.sync(compRegion.visibleProperty()));
|
||||
name.managedProperty().bind(PlatformThread.sync(compRegion.managedProperty()));
|
||||
}
|
||||
nameRegions.add(name);
|
||||
line.getChildren().add(name);
|
||||
}
|
||||
|
||||
if (entry.comp() != null) {
|
||||
var r = entry.comp().createRegion();
|
||||
compRegions.add(r);
|
||||
line.getChildren().add(r);
|
||||
compRegions.add(compRegion);
|
||||
line.getChildren().add(compRegion);
|
||||
if (!wrap) {
|
||||
HBox.setHgrow(r, Priority.ALWAYS);
|
||||
HBox.setHgrow(compRegion, Priority.ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
flow.getChildren().add(line);
|
||||
pane.getChildren().add(line);
|
||||
}
|
||||
|
||||
if (wrap) {
|
||||
|
@ -101,12 +123,12 @@ public class DynamicOptionsComp extends Comp<CompStructure<FlowPane>> {
|
|||
nameRegions.forEach(r -> r.prefWidthProperty().bind(nameWidthBinding));
|
||||
}
|
||||
|
||||
return new SimpleCompStructure<>(flow);
|
||||
return new SimpleCompStructure<>(pane);
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public record Entry(ObservableValue<String> name, Comp<?> comp) {}
|
||||
public record Entry(String key, ObservableValue<String> name, Comp<?> comp) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package io.xpipe.extension.fxcomps.impl;
|
||||
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.SimpleCompStructure;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GrowPaneComp extends Comp<CompStructure<Pane>> {
|
||||
|
||||
private final List<Comp<?>> comps;
|
||||
|
||||
public GrowPaneComp(List<Comp<?>> comps) {
|
||||
this.comps = List.copyOf(comps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<Pane> createBase() {
|
||||
var pane = new BorderPane();
|
||||
for (var c : comps) {
|
||||
pane.setCenter(c.createRegion());
|
||||
}
|
||||
pane.setPickOnBounds(false);
|
||||
return new SimpleCompStructure<>(pane);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package io.xpipe.extension.fxcomps.impl;
|
||||
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.SimpleCompStructure;
|
||||
import javafx.scene.layout.Pane;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PaneComp extends Comp<CompStructure<Pane>> {
|
||||
|
||||
private final List<Comp<?>> comps;
|
||||
|
||||
public PaneComp(List<Comp<?>> comps) {
|
||||
this.comps = List.copyOf(comps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompStructure<Pane> createBase() {
|
||||
var pane = new Pane();
|
||||
for (var c : comps) {
|
||||
pane.getChildren().add(c.createRegion());
|
||||
}
|
||||
pane.setPickOnBounds(false);
|
||||
return new SimpleCompStructure<>(pane);
|
||||
}
|
||||
}
|
|
@ -43,10 +43,14 @@ public class ToggleGroupComp<T> extends Comp<CompStructure<HBox>> {
|
|||
box.getChildren().add(b);
|
||||
b.setToggleGroup(group);
|
||||
value.addListener((c, o, n) -> {
|
||||
PlatformThread.runLaterIfNeeded(() -> b.setSelected(entry.equals(n)));
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
if (entry.getKey().equals(n)) {
|
||||
group.selectToggle(b);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (entry.getKey().equals(value.getValue())) {
|
||||
b.setSelected(true);
|
||||
group.selectToggle(b);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import io.xpipe.core.charsetter.StreamCharset;
|
|||
import io.xpipe.core.util.SecretValue;
|
||||
import io.xpipe.extension.I18n;
|
||||
import io.xpipe.extension.fxcomps.Comp;
|
||||
import io.xpipe.extension.fxcomps.CompStructure;
|
||||
import io.xpipe.extension.fxcomps.impl.*;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
|
@ -43,12 +42,14 @@ public class DynamicOptionsBuilder {
|
|||
}
|
||||
|
||||
public DynamicOptionsBuilder addTitle(String titleKey) {
|
||||
return addTitle(I18n.observable(titleKey));
|
||||
entries.add(new DynamicOptionsComp.Entry(
|
||||
titleKey, null, new LabelComp(I18n.observable(titleKey)).styleClass("title-header")));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addTitle(ObservableValue<String> title) {
|
||||
entries.add(new DynamicOptionsComp.Entry(
|
||||
null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
|
||||
null, null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -69,7 +70,7 @@ public class DynamicOptionsBuilder {
|
|||
map.put(e, I18n.observable("extension." + e.getId()));
|
||||
}
|
||||
var comp = new ChoiceComp<>(prop, map, false);
|
||||
entries.add(new DynamicOptionsComp.Entry(I18n.observable("extension.newLine"), comp));
|
||||
entries.add(new DynamicOptionsComp.Entry("newLine", I18n.observable("extension.newLine"), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -77,7 +78,7 @@ public class DynamicOptionsBuilder {
|
|||
public DynamicOptionsBuilder addCharacter(
|
||||
Property<Character> prop, ObservableValue<String> name, Map<Character, ObservableValue<String>> names) {
|
||||
var comp = new CharChoiceComp(prop, names, null);
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -88,7 +89,7 @@ public class DynamicOptionsBuilder {
|
|||
Map<Character, ObservableValue<String>> names,
|
||||
ObservableValue<String> customName) {
|
||||
var comp = new CharChoiceComp(prop, names, customName);
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -96,7 +97,7 @@ public class DynamicOptionsBuilder {
|
|||
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));
|
||||
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -104,7 +105,7 @@ public class DynamicOptionsBuilder {
|
|||
public <V> DynamicOptionsBuilder addToggle(
|
||||
Property<V> prop, ObservableValue<String> name, Map<V, ObservableValue<String>> names) {
|
||||
var comp = new ToggleGroupComp<>(prop, new SimpleObjectProperty<>(names));
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -113,7 +114,7 @@ public class DynamicOptionsBuilder {
|
|||
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));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -122,49 +123,49 @@ public class DynamicOptionsBuilder {
|
|||
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));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addCharset(Property<StreamCharset> prop) {
|
||||
var comp = new CharsetChoiceComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(I18n.observable("extension.charset"), comp));
|
||||
entries.add(new DynamicOptionsComp.Entry("charset", I18n.observable("extension.charset"), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addStringArea(String nameKey, Property<String> prop, boolean lazy) {
|
||||
var comp = new TextAreaComp(prop, lazy);
|
||||
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addString(String nameKey, Property<String> prop) {
|
||||
var comp = new TextFieldComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addString(String nameKey, Property<String> prop, boolean lazy) {
|
||||
var comp = new TextFieldComp(prop, lazy);
|
||||
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addString(ObservableValue<String> name, Property<String> prop) {
|
||||
var comp = new TextFieldComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addString(ObservableValue<String> name, Property<String> prop, boolean lazy) {
|
||||
var comp = new TextFieldComp(prop, lazy);
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -178,11 +179,13 @@ public class DynamicOptionsBuilder {
|
|||
}
|
||||
|
||||
public DynamicOptionsBuilder addComp(String nameKey, Comp<?> comp, Property<?> prop) {
|
||||
return addComp(I18n.observable(nameKey), comp, prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
|
||||
if (prop != null) props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addComp(ObservableValue<String> name, Comp<?> comp, Property<?> prop) {
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
if (prop != null) props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -193,21 +196,21 @@ public class DynamicOptionsBuilder {
|
|||
|
||||
public DynamicOptionsBuilder addSecret(ObservableValue<String> name, Property<SecretValue> prop) {
|
||||
var comp = new SecretFieldComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addInteger(ObservableValue<String> name, Property<Integer> prop) {
|
||||
var comp = new IntFieldComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(name, comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(null, name, comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicOptionsBuilder addInteger(String nameKey, Property<Integer> prop) {
|
||||
var comp = new IntFieldComp(prop);
|
||||
entries.add(new DynamicOptionsComp.Entry(I18n.observable(nameKey), comp));
|
||||
entries.add(new DynamicOptionsComp.Entry(nameKey, I18n.observable(nameKey), comp));
|
||||
props.add(prop);
|
||||
return this;
|
||||
}
|
||||
|
@ -239,12 +242,12 @@ public class DynamicOptionsBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Comp<? extends CompStructure<?>> buildComp() {
|
||||
public DynamicOptionsComp buildComp() {
|
||||
if (title != null) {
|
||||
entries.add(
|
||||
0,
|
||||
new DynamicOptionsComp.Entry(
|
||||
null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
|
||||
null, null, Comp.of(() -> new Label(title.getValue())).styleClass("title-header")));
|
||||
}
|
||||
return new DynamicOptionsComp(entries, wrap);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue