package com.kodcu.service; import com.kodcu.component.EditorPane; import com.kodcu.config.StoredConfigBean; import com.kodcu.controller.ApplicationController; import com.kodcu.other.Current; import com.kodcu.service.ui.FileBrowseService; import javafx.application.Platform; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; import java.util.stream.Stream; /** * Created by usta on 25.12.2014. */ @Component public class DirectoryService { private final ApplicationController controller; private final FileBrowseService fileBrowser; private final Current current; private final PathResolverService pathResolver; private final StoredConfigBean storedConfigBean; private final Logger logger = LoggerFactory.getLogger(DirectoryService.class); private Optional<Path> workingDirectory = Optional.of(Paths.get(System.getProperty("user.home"))); private Optional<File> initialDirectory = Optional.empty(); private Supplier<Path> pathSaveSupplier; private final FileWatchService fileWatchService; private final ThreadService threadService; @Autowired public DirectoryService(final ApplicationController controller, final FileBrowseService fileBrowser, final Current current, PathResolverService pathResolver, StoredConfigBean storedConfigBean, FileWatchService fileWatchService, ThreadService threadService) { this.controller = controller; this.fileBrowser = fileBrowser; this.current = current; this.pathResolver = pathResolver; this.storedConfigBean = storedConfigBean; this.fileWatchService = fileWatchService; this.threadService = threadService; pathSaveSupplier = () -> { final FileChooser chooser = newFileChooser("Save Document"); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Asciidoc", "*.adoc", "*.asciidoc", "*.asc", "*.ad", "*.txt", "*.*")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Markdown", "*.md", "*.markdown", "*.txt", "*.*")); File file = chooser.showSaveDialog(null); return Objects.nonNull(file) ? file.toPath() : null; }; } public DirectoryChooser newDirectoryChooser(String title) { DirectoryChooser directoryChooser = new DirectoryChooser(); directoryChooser.setTitle(title); initialDirectory.ifPresent(file -> { if (Files.isDirectory(file.toPath())) directoryChooser.setInitialDirectory(file); else directoryChooser.setInitialDirectory(file.toPath().getParent().toFile()); }); return directoryChooser; } public FileChooser newFileChooser(String title) { final FileChooser fileChooser = new FileChooser(); fileChooser.setTitle(title); initialDirectory.ifPresent(file -> { if (Files.isDirectory(file.toPath())) fileChooser.setInitialDirectory(file); else fileChooser.setInitialDirectory(file.toPath().getParent().toFile()); }); return fileChooser; } public Path workingDirectory() { return workingDirectory.orElseGet(this::workingDirectorySupplier); } private Path workingDirectorySupplier() { if (!Platform.isFxApplicationThread()) { final CompletableFuture<Path> completableFuture = new CompletableFuture<>(); completableFuture.runAsync(() -> { threadService.runActionLater(() -> { try { Path path = workingDirectorySupplier(); completableFuture.complete(path); } catch (Exception e) { completableFuture.completeExceptionally(e); } }); }, threadService.executor()); return completableFuture.join(); } final DirectoryChooser directoryChooser = newDirectoryChooser("Select working directory"); final File file = directoryChooser.showDialog(null); workingDirectory = Optional.ofNullable(file.toPath()); workingDirectory.ifPresent(fileBrowser::browse); return Objects.nonNull(file) ? file.toPath() : null; } public Path currentPath() { return current.currentPath().orElseGet(pathSaveSupplier); } public Supplier<Path> getPathSaveSupplier() { return pathSaveSupplier; } public void setPathSaveSupplier(Supplier<Path> pathSaveSupplier) { this.pathSaveSupplier = pathSaveSupplier; } public void setWorkingDirectory(Optional<Path> workingDirectory) { this.workingDirectory = workingDirectory; } public Optional<Path> getWorkingDirectory() { return workingDirectory; } public Optional<File> getInitialDirectory() { return initialDirectory; } public void setInitialDirectory(Optional<File> initialDirectory) { this.initialDirectory = initialDirectory; } public void askWorkingDir() { DirectoryChooser directoryChooser = this.newDirectoryChooser("Select Working Directory"); File selectedDir = directoryChooser.showDialog(null); if (Objects.nonNull(selectedDir)) { changeWorkigDir(selectedDir.toPath()); } } public void changeWorkigDir(Path path) { if (Objects.isNull(path)) return; storedConfigBean.setWorkingDirectory(path.toString()); this.setWorkingDirectory(Optional.of(path)); fileBrowser.browse(path); this.setInitialDirectory(Optional.ofNullable(path.toFile())); controller.getStage().setTitle(String.format("AsciidocFX - %s", path)); } public void goUp() { workingDirectory.map(Path::getParent).ifPresent(this::changeWorkigDir); } public void refreshWorkingDir() { workingDirectory.ifPresent(this::changeWorkigDir); } public String interPath() { return interPath(currentParentOrWorkdir()); } public String interPath(Path path) { try { Path subpath = path.subpath(0, path.getNameCount()); return subpath.toString().replace('\\', '/'); } catch (Exception e) { return "."; } } public Path getSaveOutputPath(FileChooser.ExtensionFilter extensionFilter, boolean askPath) { if (!Platform.isFxApplicationThread()) { final CompletableFuture<Path> completableFuture = new CompletableFuture<>(); completableFuture.runAsync(() -> { threadService.runActionLater(() -> { try { Path outputPath = getSaveOutputPath(extensionFilter, askPath); completableFuture.complete(outputPath); } catch (Exception e) { completableFuture.completeExceptionally(e); } }); }, threadService.executor()); return completableFuture.join(); } boolean isNew = current.currentTab().isNew(); if (isNew) { controller.saveDoc(); } final Path currentTabPath = current.currentPath().get(); final Path currentTabPathDir = currentTabPath.getParent(); String tabText = current.getCurrentTabText().replace("*", "").trim(); tabText = tabText.contains(".") ? tabText.split("\\.")[0] : tabText; if (!askPath) { return currentTabPathDir.resolve(extensionFilter.getExtensions().get(0).replace("*", tabText)); } final FileChooser fileChooser = this.newFileChooser(String.format("Save %s file", extensionFilter.getDescription())); fileChooser.getExtensionFilters().addAll(extensionFilter); File file = fileChooser.showSaveDialog(null); if (Objects.isNull(file)) { return currentTabPathDir.resolve(extensionFilter.getExtensions().get(0).replace("*", tabText)); } return file.toPath(); } public Path findPathInCurrentOrWorkDir(String uri) { Optional<Path> inCurrentParent = Optional.empty(); Optional<Path> inRoot = Optional.empty(); try { inCurrentParent = Optional.ofNullable(current.currentTab().getPath().getParent()); } catch (Exception e) { //no-op } try { if (inCurrentParent.isPresent()) { inRoot = inCurrentParent.map(Path::getRoot); } else { inRoot = workingDirectory.map(Path::getRoot); } } catch (Exception e) { //no-op } return Stream.<Optional<Path>>of(inCurrentParent, workingDirectory, inRoot) .filter(Optional::isPresent) .map(Optional::get) .map(path -> path.resolve(uri)) .filter(Files::exists) .findFirst() .orElseGet(() -> null); } public Path findPathInPublic(String uri) { Path configPath = controller.getConfigPath().resolve("public"); return configPath.resolve(uri); } public Path findPathInWorkdirOrLookup(Path uri) { Path workingDirectory = workingDirectory(); Path resolve = workingDirectory.resolve(uri); if (Files.exists(resolve)) { return resolve; } Optional<Path> optional = current.currentPath().map(Path::getParent); if (optional.isPresent()) { Path currentParent = optional.get(); while (true) { if (Objects.nonNull(currentParent)) { Path candidate = currentParent.resolve(uri); if (Files.exists(candidate)) { return candidate; } else { currentParent = currentParent.getParent(); } } else { break; } } } return null; } public Path currentParentOrWorkdir() { return current.currentPath().map(Path::getParent).orElse(this.workingDirectory()); } }