Merge branch 'browser'

This commit is contained in:
crschnick
2023-02-19 17:44:47 +00:00
parent b3a5b427fe
commit 211b516b7b
374 changed files with 4228 additions and 2058 deletions
@@ -1,39 +0,0 @@
package io.xpipe.core.impl;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.FilenameStore;
import io.xpipe.core.store.StreamDataStore;
public abstract class CollectionEntryDataStore implements FilenameStore, StreamDataStore {
private final boolean directory;
private final String name;
private final DataStore collectionStore;
public CollectionEntryDataStore(boolean directory, String name, DataStore collectionStore) {
this.directory = directory;
this.name = name;
this.collectionStore = collectionStore;
}
@Override
public String getFileName() {
return name;
}
public boolean isDirectory() {
return directory;
}
public DataStore getCollectionStore() {
return collectionStore;
}
public String getName() {
return name;
}
public String getPreferredProvider() {
return null;
}
}
@@ -10,7 +10,8 @@ public class FileNames {
if (split.length == 0) {
return "";
}
return split[split.length - 1];
var components = Arrays.stream(split).filter(s -> !s.isEmpty()).toList();
return components.get(components.size() - 1);
}
public static String join(String... parts) {
@@ -38,6 +39,10 @@ public class FileNames {
return normalize(file).startsWith(normalize(start));
}
public static String relativize(String from, String to) {
return normalize(to).substring(normalize(from).length());
}
public static String normalize(String file) {
var backslash = file.contains("\\");
return backslash ? toWindows(file) : toUnix(file);
@@ -72,21 +72,21 @@ public class FileStore extends JacksonizedValue implements FilenameStore, Stream
@Override
public InputStream openInput() throws Exception {
return fileSystem.openInput(file);
return fileSystem.createFileSystem().open().openInput(file);
}
@Override
public OutputStream openOutput() throws Exception {
if (!fileSystem.mkdirs(getParent())) {
if (!fileSystem.createFileSystem().open().mkdirs(getParent())) {
throw new IOException("Unable to create directory: " + getParent());
}
return fileSystem.openOutput(file);
return fileSystem.createFileSystem().open().openOutput(file);
}
@Override
public boolean canOpen() throws Exception {
return fileSystem.exists(file);
return fileSystem.createFileSystem().open().exists(file);
}
@Override
@@ -3,14 +3,16 @@ package io.xpipe.core.impl;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.process.ProcessControlProvider;
import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.store.FileSystemStore;
import io.xpipe.core.store.MachineStore;
import io.xpipe.core.store.*;
import io.xpipe.core.util.JacksonizedValue;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
@JsonTypeName("local")
public class LocalStore extends JacksonizedValue implements FileSystemStore, MachineStore {
@@ -21,30 +23,80 @@ public class LocalStore extends JacksonizedValue implements FileSystemStore, Mac
}
@Override
public boolean exists(String file) {
return Files.exists(Path.of(file));
}
public FileSystem createFileSystem() {
if (true) return new ConnectionFileSystem(ShellStore.local().create());
return new FileSystem() {
@Override
public FileSystem open() throws Exception {
return this;
}
@Override
public boolean mkdirs(String file) throws Exception {
try {
Files.createDirectories(Path.of(file));
return true;
} catch (Exception ex) {
return false;
}
}
@Override
public boolean exists(String file) {
return Files.exists(Path.of(file));
}
@Override
public InputStream openInput(String file) throws Exception {
var p = Path.of(file);
return Files.newInputStream(p);
}
@Override
public void delete(String file) throws Exception {
@Override
public OutputStream openOutput(String file) throws Exception {
var p = Path.of(file);
return Files.newOutputStream(p);
}
@Override
public void copy(String file, String newFile) throws Exception {
}
@Override
public void move(String file, String newFile) throws Exception {
}
@Override
public boolean mkdirs(String file) throws Exception {
try {
Files.createDirectories(Path.of(file));
return true;
} catch (Exception ex) {
return false;
}
}
@Override
public void touch(String file) throws Exception {
}
@Override
public boolean isDirectory(String file) throws Exception {
return Files.isDirectory(Path.of(file));
}
@Override
public Stream<FileEntry> listFiles(String file) throws Exception {
return null;
}
@Override
public List<String> listRoots() throws Exception {
return null;
}
@Override
public InputStream openInput(String file) throws Exception {
var p = Path.of(file);
return Files.newInputStream(p);
}
@Override
public OutputStream openOutput(String file) throws Exception {
var p = Path.of(file);
return Files.newOutputStream(p);
}
@Override
public void close() throws IOException {
}
};
}
@Override
@@ -1,5 +1,8 @@
package io.xpipe.core.process;
import io.xpipe.core.charsetter.Charsetter;
import lombok.SneakyThrows;
import java.io.*;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicReference;
@@ -14,6 +17,10 @@ public interface CommandProcessControl extends ProcessControl {
CommandProcessControl complex();
CommandProcessControl workingDirectory(String directory);
ShellProcessControl getParent();
default InputStream startExternalStdout() throws Exception {
try {
start();
@@ -23,11 +30,13 @@ public interface CommandProcessControl extends ProcessControl {
return new FilterInputStream(getStdout()) {
@Override
@SneakyThrows
public void close() throws IOException {
CommandProcessControl.this.close();
if (!err.get().isEmpty()) {
throw new IOException(err.get());
}
CommandProcessControl.this.getParent().restart();
}
};
} catch (Exception ex) {
@@ -43,9 +52,11 @@ public interface CommandProcessControl extends ProcessControl {
discardErr();
return new FilterOutputStream(getStdin()) {
@Override
@SneakyThrows
public void close() throws IOException {
closeStdin();
CommandProcessControl.this.close();
CommandProcessControl.this.getParent().restart();
}
};
} catch (Exception ex) {
@@ -67,6 +78,7 @@ public interface CommandProcessControl extends ProcessControl {
CommandProcessControl exitTimeout(Integer timeout);
public void withStdoutOrThrow(Charsetter.FailableConsumer<InputStreamReader, Exception> c) throws Exception;
String readOnlyStdout() throws Exception;
public void discardOrThrow() throws Exception;
@@ -77,14 +89,6 @@ public interface CommandProcessControl extends ProcessControl {
public String readOrThrow() throws Exception;
public default boolean startAndCheckExit() {
try (var pc = start()) {
return pc.discardAndCheckExit();
} catch (Exception e) {
return false;
}
}
public default boolean discardAndCheckExit() {
try {
discardOrThrow();
@@ -91,6 +91,8 @@ public interface ShellProcessControl extends ProcessControl {
void executeLine(String command) throws Exception;
void cd(String directory) throws Exception;
@Override
ShellProcessControl start() throws Exception;
@@ -2,19 +2,29 @@ package io.xpipe.core.process;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.xpipe.core.charsetter.NewLine;
import io.xpipe.core.store.FileSystem;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
public interface ShellType {
default String getCdCommand(String directory){
return "cd \"" + directory + "\"";
}
String getScriptFileEnding();
String addInlineVariablesToCommand(Map<String, String> variables, String command);
Stream<FileSystem.FileEntry> listFiles(FileSystem fs, ShellProcessControl control, String dir) throws Exception;
Stream<String> listRoots(ShellProcessControl control) throws Exception;
String getPauseCommand();
String prepareScriptContent(String content);
@@ -77,6 +87,12 @@ public interface ShellType {
String getFileReadCommand(String file);
String getPrintWorkingDirectoryCommand();
String getFileCopyCommand(String oldFile, String newFile);
String getFileMoveCommand(String oldFile, String newFile);
String getStreamFileWriteCommand(String file);
String getTextFileWriteCommand(String content, String file);
@@ -1,32 +1,16 @@
package io.xpipe.core.source;
import io.xpipe.core.store.DataStore;
import lombok.Singular;
import lombok.experimental.SuperBuilder;
import java.util.Map;
@SuperBuilder
public abstract class CollectionDataSource<DS extends DataStore> extends DataSource<DS> {
@Singular
private final Map<String, String> preferredProviders;
@Override
public DataSourceType getType() {
return DataSourceType.COLLECTION;
}
public CollectionDataSource<DS> annotate(String file, String provider) {
preferredProviders.put(file, provider);
return this;
}
public CollectionDataSource<DS> annotate(Map<String, String> preferredProviders) {
this.preferredProviders.putAll(preferredProviders);
return this;
}
public final CollectionReadConnection openReadConnection() throws Exception {
if (!isComplete()) {
throw new UnsupportedOperationException();
@@ -46,5 +30,5 @@ public abstract class CollectionDataSource<DS extends DataStore> extends DataSou
protected abstract CollectionWriteConnection newWriteConnection(WriteMode mode);
protected abstract CollectionReadConnection newReadConnection();
protected abstract CollectionReadConnection newReadConnection() throws Exception;
}
@@ -1,13 +1,12 @@
package io.xpipe.core.source;
import io.xpipe.core.impl.CollectionEntryDataStore;
import lombok.SneakyThrows;
import java.util.stream.Stream;
public interface CollectionReadConnection extends DataSourceReadConnection {
Stream<CollectionEntryDataStore> listEntries() throws Exception;
Stream<DataSource<?>> listEntries() throws Exception;
@SneakyThrows
default void forward(DataSourceConnection con) throws Exception {
@@ -0,0 +1,115 @@
package io.xpipe.core.store;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.xpipe.core.process.ShellProcessControl;
import lombok.Getter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.stream.Stream;
@Getter
public class ConnectionFileSystem implements FileSystem {
@JsonIgnore
private final ShellProcessControl shellProcessControl;
public ConnectionFileSystem(ShellProcessControl shellProcessControl) {
this.shellProcessControl = shellProcessControl;
}
@Override
public List<String> listRoots() throws Exception {
return shellProcessControl.getShellType().listRoots(shellProcessControl).toList();
}
@Override
public boolean isDirectory(String file) throws Exception{return true;}
@Override
public Stream<FileEntry> listFiles(String file) throws Exception {
return shellProcessControl.getShellType().listFiles(this, shellProcessControl, file);
}
@Override
public FileSystem open() throws Exception {
shellProcessControl.start();
return this;
}
@Override
public InputStream openInput(String file) throws Exception {
return shellProcessControl.command(proc ->
proc.getShellType().getFileReadCommand(proc.getOsType().normalizeFileName(file)))
.startExternalStdout();
}
@Override
public OutputStream openOutput(String file) throws Exception {
return shellProcessControl.command(proc -> proc.getShellType()
.getStreamFileWriteCommand(proc.getOsType().normalizeFileName(file)))
.startExternalStdin();
}
@Override
public boolean exists(String file) throws Exception {
try (var pc = shellProcessControl.command(proc -> proc.getShellType()
.getFileExistsCommand(proc.getOsType().normalizeFileName(file)))
.start()) {
return pc.discardAndCheckExit();
}
}
@Override
public void delete(String file) throws Exception {
try (var pc = shellProcessControl.command(proc -> proc.getShellType()
.getFileDeleteCommand(proc.getOsType().normalizeFileName(file)))
.start()) {
pc.discardOrThrow();
}
}
@Override
public void copy(String file, String newFile) throws Exception {
try (var pc = shellProcessControl.command(proc -> proc.getShellType()
.getFileCopyCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex()
.start()) {
pc.discardOrThrow();
}
}
@Override
public void move(String file, String newFile) throws Exception {
try (var pc = shellProcessControl.command(proc -> proc.getShellType()
.getFileMoveCommand(proc.getOsType().normalizeFileName(file), proc.getOsType().normalizeFileName(newFile))).complex()
.start()) {
pc.discardOrThrow();
}
}
@Override
public boolean mkdirs(String file) throws Exception {
try (var pc = shellProcessControl.command(proc -> proc.getShellType()
.flatten(proc.getShellType()
.getMkdirsCommand(proc.getOsType().normalizeFileName(file))))
.start()) {
return pc.discardAndCheckExit();
}
}
@Override
public void touch(String file) throws Exception {
try (var pc = shellProcessControl.command(proc -> proc.getShellType()
.getFileTouchCommand(proc.getOsType().normalizeFileName(file))).complex()
.start()) {
pc.discardOrThrow();
}
}
@Override
public void close() throws IOException {
shellProcessControl.close();
}
}
@@ -0,0 +1,61 @@
package io.xpipe.core.store;
import lombok.NonNull;
import lombok.Value;
import java.io.Closeable;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.Instant;
import java.util.List;
import java.util.stream.Stream;
public interface FileSystem extends Closeable, AutoCloseable {
@Value
static class FileEntry {
@NonNull
FileSystem fileSystem;
@NonNull
String path;
@NonNull
Instant date;
boolean directory;
boolean hidden;
long size;
}
FileSystem open() throws Exception;
InputStream openInput(String file) throws Exception;
OutputStream openOutput(String file) throws Exception;
public boolean exists(String file) throws Exception;
public void delete(String file) throws Exception;
void copy(String file, String newFile) throws Exception;
void move(String file, String newFile) throws Exception;
boolean mkdirs(String file) throws Exception;
void touch(String file) throws Exception;
boolean isDirectory(String file) throws Exception;
Stream<FileEntry> listFiles(String file) throws Exception;
default Stream<FileEntry> listFilesRecursively(String file) throws Exception {
return listFiles(file).flatMap(fileEntry -> {
try {
return listFilesRecursively(fileEntry.getPath());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
List<String> listRoots() throws Exception;
}
@@ -1,15 +1,6 @@
package io.xpipe.core.store;
import java.io.InputStream;
import java.io.OutputStream;
public interface FileSystemStore extends DataStore {
InputStream openInput(String file) throws Exception;
OutputStream openOutput(String file) throws Exception;
public boolean exists(String file) throws Exception;
boolean mkdirs(String file) throws Exception;
FileSystem createFileSystem();
}
@@ -1,8 +1,5 @@
package io.xpipe.core.store;
import java.io.InputStream;
import java.io.OutputStream;
public interface MachineStore extends FileSystemStore, ShellStore {
public default boolean isLocal() {
@@ -10,35 +7,7 @@ public interface MachineStore extends FileSystemStore, ShellStore {
}
@Override
public default InputStream openInput(String file) throws Exception {
return create().command(proc ->
proc.getShellType().getFileReadCommand(proc.getOsType().normalizeFileName(file)))
.startExternalStdout();
}
@Override
public default OutputStream openOutput(String file) throws Exception {
return create().command(proc -> proc.getShellType()
.getStreamFileWriteCommand(proc.getOsType().normalizeFileName(file)))
.startExternalStdin();
}
@Override
public default boolean exists(String file) throws Exception {
try (var pc = create().command(proc -> proc.getShellType()
.getFileExistsCommand(proc.getOsType().normalizeFileName(file)))
.start()) {
return pc.discardAndCheckExit();
}
}
@Override
public default boolean mkdirs(String file) throws Exception {
try (var pc = create().command(proc -> proc.getShellType()
.flatten(proc.getShellType()
.getMkdirsCommand(proc.getOsType().normalizeFileName(file))))
.start()) {
return pc.discardAndCheckExit();
}
default FileSystem createFileSystem() {
return new ConnectionFileSystem(create());
}
}
@@ -8,7 +8,7 @@ import io.xpipe.core.process.ShellType;
import java.nio.charset.Charset;
public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStore {
public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStore, FileSystemStore {
public static MachineStore local() {
return new LocalStore();
@@ -24,6 +24,11 @@ public interface ShellStore extends DataStore, StatefulDataStore, LaunchableStor
return s instanceof LocalStore;
}
@Override
default FileSystem createFileSystem() {
return new ConnectionFileSystem(create());
}
@Override
default String prepareLaunchCommand() throws Exception {
return create().prepareTerminalOpen();
@@ -40,7 +40,6 @@ public class CoreJacksonModule extends SimpleModule {
new NamedType(StdinDataStore.class),
new NamedType(StdoutDataStore.class),
new NamedType(LocalDirectoryDataStore.class),
new NamedType(CollectionEntryDataStore.class),
new NamedType(LocalStore.class),
new NamedType(NamedStore.class),
new NamedType(ValueType.class),
@@ -84,7 +83,7 @@ public class CoreJacksonModule extends SimpleModule {
TODO: Find better way to supply a default serializer for data sources
*/
try {
Class.forName("io.xpipe.extension.ExtensionException");
Class.forName("io.xpipe.app.core.App");
} catch (ClassNotFoundException e) {
addSerializer(DataSource.class, new NullSerializer());
addDeserializer(DataSource.class, new NullDeserializer());
@@ -34,7 +34,7 @@ public class XPipeInstallation {
}
@SneakyThrows
public static Path getLocalInstallationBasePath() {
public static Path getCurrentInstallationBasePath() {
Path path =
Path.of(ProcessHandle.current().info().command().orElseThrow()).toRealPath();
if (!path.isAbsolute()) {
@@ -54,7 +54,7 @@ public class XPipeInstallation {
}
public static boolean isInstallationDistribution() {
var base = getLocalInstallationBasePath();
var base = getCurrentInstallationBasePath();
if (OsType.getLocal().equals(OsType.MACOS)) {
if (!base.toString().equals(getLocalDefaultInstallationBasePath(false))) {
return false;
@@ -77,7 +77,7 @@ public class XPipeInstallation {
}
public static Path getLocalDynamicLibraryDirectory() {
Path path = getLocalInstallationBasePath();
Path path = getCurrentInstallationBasePath();
if (OsType.getLocal().equals(OsType.WINDOWS)) {
return path.resolve("app").resolve("runtime").resolve("bin");
} else if (OsType.getLocal().equals(OsType.LINUX)) {
@@ -91,8 +91,7 @@ public class XPipeInstallation {
}
}
public static Path getLocalExtensionsDirectory() {
Path path = getLocalInstallationBasePath();
public static Path getLocalExtensionsDirectory(Path path) {
return OsType.getLocal().equals(OsType.MACOS)
? path.resolve("Contents").resolve("Resources").resolve("extensions")
: path.resolve("app").resolve("extensions");
@@ -178,7 +177,7 @@ public class XPipeInstallation {
}
public static Path getLocalDefaultInstallationIcon() {
Path path = getLocalInstallationBasePath();
Path path = getCurrentInstallationBasePath();
// Check for development environment
if (!ModuleHelper.isImage()) {