mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
Rework file references for ssh keys
This commit is contained in:
parent
7574ff8666
commit
e1f62830b1
11 changed files with 101 additions and 156 deletions
|
@ -5,7 +5,7 @@ import io.xpipe.app.storage.DataStorage;
|
|||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.ThreadHelper;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import io.xpipe.app.util.FileReference;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -30,7 +30,7 @@ public class BrowserModel {
|
|||
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel(this);
|
||||
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
|
||||
@Setter
|
||||
private Consumer<List<FileStore>> onFinish;
|
||||
private Consumer<List<FileReference>> onFinish;
|
||||
|
||||
public BrowserModel(Mode mode) {
|
||||
this.mode = mode;
|
||||
|
@ -100,7 +100,7 @@ public class BrowserModel {
|
|||
}
|
||||
|
||||
var stores = chosen.stream().map(
|
||||
entry -> new FileStore(entry.getRawFileEntry().getFileSystem().getStore(), entry.getRawFileEntry().getPath())).toList();
|
||||
entry -> new FileReference(selected.getValue().getEntry(), entry.getRawFileEntry().getPath())).toList();
|
||||
onFinish.accept(stores);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import io.xpipe.app.core.AppI18n;
|
|||
import io.xpipe.app.core.AppWindowHelper;
|
||||
import io.xpipe.app.fxcomps.util.PlatformThread;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import io.xpipe.app.util.FileReference;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.stage.FileChooser;
|
||||
|
@ -20,7 +20,7 @@ import java.util.function.Supplier;
|
|||
public class StandaloneFileBrowser {
|
||||
|
||||
public static void localOpenFileChooser(
|
||||
Property<FileStore> fileStoreProperty, Window owner, Map<String, List<String>> extensions) {
|
||||
Property<FileReference> fileStoreProperty, Window owner, Map<String, List<String>> extensions) {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(AppI18n.get("browseFileTitle"));
|
||||
|
@ -34,12 +34,12 @@ public class StandaloneFileBrowser {
|
|||
|
||||
File file = fileChooser.showOpenDialog(owner);
|
||||
if (file != null && file.exists()) {
|
||||
fileStoreProperty.setValue(FileStore.local(file.toPath()));
|
||||
fileStoreProperty.setValue(FileReference.local(file.toPath()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void openSingleFile(Supplier<DataStoreEntryRef<? extends FileSystemStore>> store, Consumer<FileStore> file) {
|
||||
public static void openSingleFile(Supplier<DataStoreEntryRef<? extends FileSystemStore>> store, Consumer<FileReference> file) {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
var model = new BrowserModel(BrowserModel.Mode.SINGLE_FILE_CHOOSER);
|
||||
var comp = new BrowserComp(model)
|
||||
|
@ -55,7 +55,7 @@ public class StandaloneFileBrowser {
|
|||
});
|
||||
}
|
||||
|
||||
public static void saveSingleFile(Property<FileStore> file) {
|
||||
public static void saveSingleFile(Property<FileReference> file) {
|
||||
PlatformThread.runLaterIfNeeded(() -> {
|
||||
var model = new BrowserModel(BrowserModel.Mode.SINGLE_FILE_SAVE);
|
||||
var comp = new BrowserComp(model)
|
||||
|
|
|
@ -4,11 +4,13 @@ import atlantafx.base.theme.Styles;
|
|||
import io.xpipe.app.browser.StandaloneFileBrowser;
|
||||
import io.xpipe.app.comp.base.ButtonComp;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.fxcomps.util.SimpleChangeListener;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
|
@ -16,13 +18,22 @@ import org.kordamp.ikonli.javafx.FontIcon;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public class FileStoreChoiceComp extends SimpleComp {
|
||||
public class FileReferenceChoiceComp extends SimpleComp {
|
||||
|
||||
private final boolean hideFileSystem;
|
||||
private final Property<FileSystemStore> fileSystem;
|
||||
private final Property<DataStoreEntryRef<? extends FileSystemStore>> fileSystem;
|
||||
private final Property<String> filePath;
|
||||
|
||||
public FileStoreChoiceComp(boolean hideFileSystem, Property<FileSystemStore> fileSystem, Property<String> filePath) {
|
||||
public <T extends FileSystemStore> FileReferenceChoiceComp(ObservableValue<DataStoreEntryRef<T>> fileSystem, Property<String> filePath) {
|
||||
this.hideFileSystem = true;
|
||||
this.fileSystem = new SimpleObjectProperty<>();
|
||||
SimpleChangeListener.apply(fileSystem, val -> {
|
||||
this.fileSystem.setValue(val);
|
||||
});
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public FileReferenceChoiceComp(boolean hideFileSystem, Property<DataStoreEntryRef<? extends FileSystemStore>> fileSystem, Property<String> filePath) {
|
||||
this.hideFileSystem = hideFileSystem;
|
||||
this.fileSystem = fileSystem != null ? fileSystem : new SimpleObjectProperty<>();
|
||||
this.filePath = filePath;
|
||||
|
@ -42,7 +53,7 @@ public class FileStoreChoiceComp extends SimpleComp {
|
|||
.grow(false, true);
|
||||
|
||||
var fileBrowseButton = new ButtonComp(null, new FontIcon("mdi2f-folder-open-outline"), () -> {
|
||||
StandaloneFileBrowser.openSingleFile(() -> hideFileSystem ? DataStorage.get().local().ref() : null, fileStore -> {
|
||||
StandaloneFileBrowser.openSingleFile(() -> hideFileSystem ? fileSystem.getValue() : null, fileStore -> {
|
||||
if (fileStore == null) {
|
||||
filePath.setValue(null);
|
||||
fileSystem.setValue(null);
|
|
@ -1,8 +1,9 @@
|
|||
package io.xpipe.app.fxcomps.impl;
|
||||
|
||||
import io.xpipe.app.ext.DataStoreProviders;
|
||||
import io.xpipe.app.fxcomps.SimpleComp;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.CustomComboBoxBuilder;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import javafx.beans.property.Property;
|
||||
|
@ -13,40 +14,36 @@ import javafx.scene.layout.Region;
|
|||
|
||||
public class FileSystemStoreChoiceComp extends SimpleComp {
|
||||
|
||||
private final Property<FileSystemStore> selected;
|
||||
private final Property<DataStoreEntryRef<? extends FileSystemStore>> selected;
|
||||
|
||||
public FileSystemStoreChoiceComp(Property<FileSystemStore> selected) {
|
||||
public FileSystemStoreChoiceComp(Property<DataStoreEntryRef<? extends FileSystemStore>> selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
private static String getName(FileSystemStore store) {
|
||||
return DataStorage.get().getUsableStores().stream()
|
||||
.filter(e -> e.equals(store))
|
||||
.findAny()
|
||||
.map(e -> DataStorage.get().getStoreDisplayName(e).orElse("?"))
|
||||
.orElse("?");
|
||||
private static String getName(DataStoreEntryRef<? extends FileSystemStore> store) {
|
||||
return store.get().getName();
|
||||
}
|
||||
|
||||
private Region createGraphic(FileSystemStore s) {
|
||||
var provider = DataStoreProviders.byStore(s);
|
||||
var img = PrettyImageHelper.ofFixedSquare(provider.getDisplayIconFileName(s), 16);
|
||||
private Region createGraphic(DataStoreEntryRef<? extends FileSystemStore> s) {
|
||||
var provider = s.get().getProvider();
|
||||
var img = PrettyImageHelper.ofFixedSquare(provider.getDisplayIconFileName(s.getStore()), 16);
|
||||
return new Label(getName(s), img.createRegion());
|
||||
}
|
||||
|
||||
private Region createDisplayGraphic(FileSystemStore s) {
|
||||
var provider = DataStoreProviders.byStore(s);
|
||||
var img = PrettyImageHelper.ofFixedSquare(provider.getDisplayIconFileName(s), 16);
|
||||
private Region createDisplayGraphic(DataStoreEntryRef<? extends FileSystemStore> s) {
|
||||
var provider = s.get().getProvider();
|
||||
var img = PrettyImageHelper.ofFixedSquare(provider.getDisplayIconFileName(s.getStore()), 16);
|
||||
return new Label(null, img.createRegion());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Region createSimple() {
|
||||
var comboBox = new CustomComboBoxBuilder<>(selected, this::createGraphic, null, v -> true);
|
||||
comboBox.setAccessibleNames(store -> getName(store));
|
||||
comboBox.setAccessibleNames(FileSystemStoreChoiceComp::getName);
|
||||
comboBox.setSelectedDisplay(this::createDisplayGraphic);
|
||||
DataStorage.get().getUsableStores().stream()
|
||||
.filter(e -> e instanceof FileSystemStore)
|
||||
.map(e -> (FileSystemStore) e)
|
||||
DataStorage.get().getUsableEntries().stream()
|
||||
.filter(e -> e.getStore() instanceof FileSystemStore)
|
||||
.map(DataStoreEntry::<FileSystemStore>ref)
|
||||
.forEach(comboBox::add);
|
||||
ComboBox<Node> cb = comboBox.build();
|
||||
cb.getStyleClass().add("choice-comp");
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.nio.file.Path;
|
|||
import java.util.regex.Matcher;
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class LocalFileReference {
|
||||
public class ContextualFileReference {
|
||||
|
||||
private static String lastDataDir;
|
||||
|
||||
|
@ -36,7 +36,7 @@ public class LocalFileReference {
|
|||
}
|
||||
}
|
||||
|
||||
public static LocalFileReference of(String s) {
|
||||
public static ContextualFileReference of(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public class LocalFileReference {
|
|||
// Replacement order is important
|
||||
var replaced = ns.replace("<DATA>", getDataDir())
|
||||
.replace("~", normalized(System.getProperty("user.home")));
|
||||
return new LocalFileReference(normalized(replaced));
|
||||
return new ContextualFileReference(normalized(replaced));
|
||||
}
|
||||
|
||||
@NonNull
|
|
@ -550,10 +550,9 @@ public abstract class DataStorage {
|
|||
return children;
|
||||
}
|
||||
|
||||
public List<DataStore> getUsableStores() {
|
||||
public List<DataStoreEntry> getUsableEntries() {
|
||||
return new ArrayList<>(getStoreEntries().stream()
|
||||
.filter(entry -> entry.getValidity().isUsable())
|
||||
.map(DataStoreEntry::getStore)
|
||||
.toList());
|
||||
}
|
||||
|
||||
|
|
|
@ -19,25 +19,25 @@ public class StorageJacksonModule extends SimpleModule {
|
|||
public void setupModule(SetupContext context) {
|
||||
addSerializer(DataStoreEntryRef.class, new DataStoreEntryRefSerializer());
|
||||
addDeserializer(DataStoreEntryRef.class, new DataStoreEntryRefDeserializer());
|
||||
addSerializer(LocalFileReference.class, new LocalFileReferenceSerializer());
|
||||
addDeserializer(LocalFileReference.class, new LocalFileReferenceDeserializer());
|
||||
addSerializer(ContextualFileReference.class, new LocalFileReferenceSerializer());
|
||||
addDeserializer(ContextualFileReference.class, new LocalFileReferenceDeserializer());
|
||||
context.addSerializers(_serializers);
|
||||
context.addDeserializers(_deserializers);
|
||||
}
|
||||
|
||||
public static class LocalFileReferenceSerializer extends JsonSerializer<LocalFileReference> {
|
||||
public static class LocalFileReferenceSerializer extends JsonSerializer<ContextualFileReference> {
|
||||
|
||||
@Override
|
||||
public void serialize(LocalFileReference value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
public void serialize(ContextualFileReference value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
|
||||
jgen.writeString(value.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
public static class LocalFileReferenceDeserializer extends JsonDeserializer<LocalFileReference> {
|
||||
public static class LocalFileReferenceDeserializer extends JsonDeserializer<ContextualFileReference> {
|
||||
|
||||
@Override
|
||||
public LocalFileReference deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return LocalFileReference.of(p.getValueAsString());
|
||||
public ContextualFileReference deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||
return ContextualFileReference.of(p.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package io.xpipe.app.test;
|
||||
|
||||
import io.xpipe.core.store.DataStore;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -16,14 +14,4 @@ public class ExtensionTest {
|
|||
}
|
||||
return Path.of(url.toURI());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static DataStore getResourceStore(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 = Path.of(url.toURI()).toString();
|
||||
return FileStore.local(Path.of(file));
|
||||
}
|
||||
}
|
||||
|
|
43
app/src/main/java/io/xpipe/app/util/FileReference.java
Normal file
43
app/src/main/java/io/xpipe/app/util/FileReference.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.core.store.FileSystemStore;
|
||||
import io.xpipe.core.store.LocalStore;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Represents a file located on a file system.
|
||||
*/
|
||||
@JsonTypeName("file")
|
||||
@SuperBuilder
|
||||
@Jacksonized
|
||||
@Getter
|
||||
public class FileReference extends JacksonizedValue {
|
||||
|
||||
DataStoreEntryRef<? extends FileSystemStore> fileSystem;
|
||||
String path;
|
||||
|
||||
public FileReference(DataStoreEntryRef<? extends FileSystemStore> fileSystem, String path) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public static FileReference local(Path p) {
|
||||
return new FileReference(DataStorage.get().local().ref(), p.toString());
|
||||
}
|
||||
|
||||
public static FileReference local(String p) {
|
||||
return new FileReference(DataStorage.get().local().ref(), p);
|
||||
}
|
||||
|
||||
public final boolean isLocal() {
|
||||
return fileSystem.getStore() instanceof LocalStore;
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
package io.xpipe.core.store;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonTypeName;
|
||||
import io.xpipe.core.util.JacksonizedValue;
|
||||
import io.xpipe.core.util.ValidationException;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Represents a file located on a file system.
|
||||
*/
|
||||
@JsonTypeName("file")
|
||||
@SuperBuilder
|
||||
@Jacksonized
|
||||
@Getter
|
||||
public class FileStore extends JacksonizedValue implements FilenameStore, StreamDataStore {
|
||||
|
||||
FileSystemStore fileSystem;
|
||||
String path;
|
||||
|
||||
public FileStore(FileSystemStore fileSystem, String path) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public static FileStore local(Path p) {
|
||||
return new FileStore(new LocalStore(), p.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file store for a file that is local to the callers machine.
|
||||
*/
|
||||
public static FileStore local(String p) {
|
||||
return new FileStore(new LocalStore(), p);
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
var matcher = Pattern.compile("^(.+?)[^\\\\/]+$").matcher(path);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException("Unable to determine parent of " + path);
|
||||
}
|
||||
|
||||
return matcher.group(1);
|
||||
}
|
||||
|
||||
public final boolean isLocal() {
|
||||
return fileSystem instanceof LocalStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkComplete() throws Exception {
|
||||
if (fileSystem == null) {
|
||||
throw new ValidationException("File system is missing");
|
||||
}
|
||||
if (path == null) {
|
||||
throw new ValidationException("File is missing");
|
||||
}
|
||||
if (!FileNames.isAbsolute(path)) {
|
||||
throw new ValidationException("File path is not absolute");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openInput() throws Exception {
|
||||
return fileSystem.createFileSystem().open().openInput(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream openOutput() throws Exception {
|
||||
fileSystem.createFileSystem().open().mkdirs(getParent());
|
||||
return fileSystem.createFileSystem().open().openOutput(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOpen() throws Exception {
|
||||
return fileSystem.createFileSystem().open().fileExists(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
var split = path.split("[\\\\/]");
|
||||
if (split.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return split[split.length - 1];
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import io.xpipe.api.DataSource;
|
|||
import io.xpipe.app.test.DaemonExtensionTest;
|
||||
import io.xpipe.core.charsetter.NewLine;
|
||||
import io.xpipe.core.charsetter.StreamCharset;
|
||||
import io.xpipe.core.store.FileStore;
|
||||
import io.xpipe.app.util.FileReference;
|
||||
import io.xpipe.core.impl.TextSource;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -38,15 +38,15 @@ public class TextFileTest extends DaemonExtensionTest {
|
|||
@BeforeAll
|
||||
public static void setupStorage() throws Exception {
|
||||
utf8 = getSource("text", "utf8-bom-lf.txt");
|
||||
utf16 = DataSource.create(null, "text", FileStore.local(utf16File));
|
||||
appendReference = DataSource.create(null, "text", FileStore.local(appendReferenceFile));
|
||||
writeReference = DataSource.create(null, "text", FileStore.local(writeReferenceFile));
|
||||
utf16 = DataSource.create(null, "text", FileReference.local(utf16File));
|
||||
appendReference = DataSource.create(null, "text", FileReference.local(appendReferenceFile));
|
||||
writeReference = DataSource.create(null, "text", FileReference.local(writeReferenceFile));
|
||||
|
||||
appendOutputFile = Files.createTempFile(null, null);
|
||||
appendOutput = DataSource.create(
|
||||
null,
|
||||
TextSource.builder()
|
||||
.store(FileStore.local(appendOutputFile))
|
||||
.store(FileReference.local(appendOutputFile))
|
||||
.charset(StreamCharset.get("windows-1252"))
|
||||
.newLine(NewLine.LF)
|
||||
.build());
|
||||
|
@ -55,7 +55,7 @@ public class TextFileTest extends DaemonExtensionTest {
|
|||
writeOutput = DataSource.create(
|
||||
null,
|
||||
TextSource.builder()
|
||||
.store(FileStore.local(writeOutputFile))
|
||||
.store(FileReference.local(writeOutputFile))
|
||||
.charset(StreamCharset.UTF16_LE_BOM)
|
||||
.newLine(NewLine.CRLF)
|
||||
.build());
|
||||
|
@ -90,7 +90,7 @@ public class TextFileTest extends DaemonExtensionTest {
|
|||
var emptySource = DataSource.create(
|
||||
null,
|
||||
TextSource.builder()
|
||||
.store(FileStore.local(empty))
|
||||
.store(FileReference.local(empty))
|
||||
.charset(StreamCharset.UTF32_BE)
|
||||
.newLine(NewLine.CRLF)
|
||||
.build());
|
||||
|
|
Loading…
Reference in a new issue