From b9e3f13cfd9b7ebf151f8d52bc3b66ca67cbf302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Thu, 25 Jun 2026 16:55:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E5=85=BC=E5=AE=B9=E6=80=A7?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workutils/WorkUtilsApplication.java | 2 + .../workutils/biz/apidoc/ApiDocWindow.java | 20 +- .../workutils/biz/git/GitSyncPlugin.java | 46 +++-- .../mybatisexport/MyBatisExportWindow.java | 20 +- .../workutils/biz/scanner/ScannerWindow.java | 24 +-- .../system/config/SettingsWindow.java | 16 +- .../system/support/SystemOpenHelper.java | 191 ++++++++++++++++++ 7 files changed, 242 insertions(+), 77 deletions(-) create mode 100644 src/main/java/com/example/workutils/system/support/SystemOpenHelper.java diff --git a/src/main/java/com/example/workutils/WorkUtilsApplication.java b/src/main/java/com/example/workutils/WorkUtilsApplication.java index 2bafa16..34ae493 100644 --- a/src/main/java/com/example/workutils/WorkUtilsApplication.java +++ b/src/main/java/com/example/workutils/WorkUtilsApplication.java @@ -6,6 +6,7 @@ import com.example.workutils.system.plugin.PluginDescriptor; import com.example.workutils.system.plugin.PluginManager; import com.example.workutils.system.plugin.VisualPluginFactory; import com.example.workutils.system.support.JfxBootstrap; +import com.example.workutils.system.support.SystemOpenHelper; import com.example.workutils.system.support.WinConsole; import com.example.workutils.system.ui.MainWindow; import com.example.workutils.system.ui.Theme; @@ -82,6 +83,7 @@ public class WorkUtilsApplication implements CommandLineRunner { } public static void main(String[] args) { + SystemOpenHelper.initialize(); WinConsole.ensureUtf8(); SpringApplication.run(WorkUtilsApplication.class, args); } diff --git a/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java b/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java index 091c84f..18337a5 100644 --- a/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java +++ b/src/main/java/com/example/workutils/biz/apidoc/ApiDocWindow.java @@ -6,6 +6,7 @@ import com.example.workutils.biz.apidoc.parser.JavaSourceParser; import com.example.workutils.system.config.WorkUtilsProperties; import com.example.workutils.system.support.FileOperationHelper; import com.example.workutils.system.support.HistoryPersistence; +import com.example.workutils.system.support.SystemOpenHelper; import com.example.workutils.system.ui.StyledButton; import com.example.workutils.system.ui.Theme; import javafx.application.Platform; @@ -22,7 +23,6 @@ import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import javafx.stage.Stage; -import java.awt.Desktop; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -678,7 +678,7 @@ public class ApiDocWindow { MenuItem defaultOpen = new MenuItem("系统默认程序打开"); defaultOpen.setOnAction(e -> openFile(currentFileName)); - MenuItem openNotepad = new MenuItem("用记事本打开"); + MenuItem openNotepad = new MenuItem("用系统编辑器打开"); openNotepad.setOnAction(e -> openWithNotepad(currentFileName)); MenuItem openFolder = new MenuItem("打开所在文件夹"); openFolder.setOnAction(e -> openFolder(currentFileName)); @@ -728,12 +728,7 @@ public class ApiDocWindow { try { File f = new File(path); if (!f.exists()) return; - String os = System.getProperty("os.name").toLowerCase(); - if (os.contains("win")) { - new ProcessBuilder("cmd", "/c", "start", "", path).start(); - } else { - Desktop.getDesktop().open(f); - } + SystemOpenHelper.openFile(f.toPath()); } catch (Exception ignored) { } } @@ -741,7 +736,7 @@ public class ApiDocWindow { private void openWithNotepad(String path) { if (path == null) return; try { - new ProcessBuilder("notepad.exe", path).start(); + SystemOpenHelper.editFile(path); } catch (Exception e) { openFile(path); } @@ -752,12 +747,7 @@ public class ApiDocWindow { try { File f = new File(path).getParentFile(); if (f == null || !f.exists()) return; - String os = System.getProperty("os.name").toLowerCase(); - if (os.contains("win")) { - new ProcessBuilder("explorer.exe", "/select,", path).start(); - } else { - Desktop.getDesktop().open(f); - } + SystemOpenHelper.revealInFileManager(path); } catch (Exception ignored) { } } diff --git a/src/main/java/com/example/workutils/biz/git/GitSyncPlugin.java b/src/main/java/com/example/workutils/biz/git/GitSyncPlugin.java index a64bbab..0be70cd 100644 --- a/src/main/java/com/example/workutils/biz/git/GitSyncPlugin.java +++ b/src/main/java/com/example/workutils/biz/git/GitSyncPlugin.java @@ -19,6 +19,7 @@ import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -543,27 +544,27 @@ public class GitSyncPlugin implements Plugin, InitializingBean { } // 1. fetch - CmdResult r1 = runGit(repoPath, "git fetch origin " + branch); + CmdResult r1 = runGit(repoPath, "git", "fetch", "origin", branch); if (!r1.ok) { log.warn("fetch 失败: {} -> {}", repoPath, r1.error); } // 2. checkout:先 stash 本地修改,避免因未提交修改导致 checkout 失败 boolean didStash = false; - CmdResult r2 = runGit(repoPath, "git checkout " + branch); + CmdResult r2 = runGit(repoPath, "git", "checkout", branch); if (!r2.ok) { // checkout 失败常见原因:有未提交修改 → stash 后重试 - CmdResult stashResult = runGit(repoPath, "git stash push -m \"workutils-auto-stash\""); + CmdResult stashResult = runGit(repoPath, "git", "stash", "push", "-m", "workutils-auto-stash"); if (stashResult.ok) { didStash = true; - r2 = runGit(repoPath, "git checkout " + branch); + r2 = runGit(repoPath, "git", "checkout", branch); } } if (!r2.ok) { log.warn("checkout 失败: {} -> {},标记为损坏", repoPath, r2.error); // 如果 stash 了但 checkout 仍失败,恢复 stash if (didStash) { - runGit(repoPath, "git stash pop"); + runGit(repoPath, "git", "stash", "pop"); } SyncResult r = SyncResult.DAMAGED; r.error = "checkout 失败: " + r2.error; @@ -572,12 +573,12 @@ public class GitSyncPlugin implements Plugin, InitializingBean { } // 3. pull - CmdResult r3 = runGit(repoPath, "git pull origin " + branch); + CmdResult r3 = runGit(repoPath, "git", "pull", "origin", branch); if (!r3.ok) { log.warn("pull 失败: {} -> {},标记为损坏", repoPath, r3.error); // pull 失败时恢复 stash(如果 stash 过) if (didStash) { - runGit(repoPath, "git stash pop"); + runGit(repoPath, "git", "stash", "pop"); } SyncResult r = SyncResult.DAMAGED; r.error = "pull 失败: " + r3.error; @@ -587,7 +588,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean { // 4. 恢复之前 stash 的修改(忽略冲突,保留 stash 让用户手动处理) if (didStash) { - CmdResult popResult = runGit(repoPath, "git stash pop"); + CmdResult popResult = runGit(repoPath, "git", "stash", "pop"); if (!popResult.ok) { log.warn("stash pop 有冲突: {} -> {},已保留在 stash 中", repoPath, popResult.error); } @@ -619,8 +620,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean { } // clone String parentDir = dir.getParent() != null ? dir.getParent() : "."; - CmdResult cr = runGit(parentDir, - "git clone -b " + branch + " " + cloneUrl + " \"" + repoPath + "\""); + CmdResult cr = runGit(parentDir, "git", "clone", "-b", branch, cloneUrl, repoPath); if (cr.ok) { return true; } else { @@ -649,16 +649,15 @@ public class GitSyncPlugin implements Plugin, InitializingBean { * 关键:进程输出在独立线程中读取,主线程通过 process.waitFor(timeout) 等待。 * 超时后强制杀进程 + 中断读线程,杜绝僵尸进程 / 永久阻塞。 */ - private CmdResult runGit(String workingDir, String command) { + private CmdResult runGit(String workingDir, String... command) { CmdResult result = new CmdResult(); Process process = null; Thread readerThread = null; StringBuilder output = new StringBuilder(); + String commandText = String.join(" ", Arrays.asList(command)); try { - ProcessBuilder pb = isWindows() - ? new ProcessBuilder("cmd.exe", "/c", command) - : new ProcessBuilder("bash", "-c", command); + ProcessBuilder pb = new ProcessBuilder(command); if (workingDir != null) { pb.directory(new File(workingDir)); @@ -685,7 +684,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean { // 等待进程结束(带超时) boolean finished = process.waitFor(CMD_TIMEOUT_SECONDS, TimeUnit.SECONDS); if (!finished) { - log.warn("Git 命令超时({}s): {} -> {}", CMD_TIMEOUT_SECONDS, workingDir, command); + log.warn("Git 命令超时({}s): {} -> {}", CMD_TIMEOUT_SECONDS, workingDir, commandText); result.ok = false; result.error = "命令超时(" + CMD_TIMEOUT_SECONDS + "s)"; return result; @@ -728,7 +727,7 @@ public class GitSyncPlugin implements Plugin, InitializingBean { private String getRemoteUrl(String repoPath, Map baseUrlMap) { // 1. 尝试 git remote get-url - CmdResult r = runGit(repoPath, "git remote get-url origin"); + CmdResult r = runGit(repoPath, "git", "remote", "get-url", "origin"); if (r.ok && !r.output.isEmpty()) { return r.output.trim(); } @@ -800,14 +799,21 @@ public class GitSyncPlugin implements Plugin, InitializingBean { return baseUrlMap.get(normalizePath(repoPath)); } - /** 路径规范化:统一用 / 分隔、小写,用于映射表匹配 */ + /** 路径规范化:统一用 / 分隔,不改变大小写,避免破坏大小写敏感文件系统。 */ private String normalizePath(String path) { if (path == null || path.isEmpty()) return ""; - return path.replace('\\', '/').toLowerCase(); + String normalized = path.trim().replace('\\', '/'); + while (normalized.length() > 1 && normalized.endsWith("/") && !isWindowsDriveRoot(normalized)) { + normalized = normalized.substring(0, normalized.length() - 1); + } + return normalized; } - private boolean isWindows() { - return System.getProperty("os.name").toLowerCase().contains("win"); + private boolean isWindowsDriveRoot(String path) { + return path.length() == 3 + && Character.isLetter(path.charAt(0)) + && path.charAt(1) == ':' + && path.charAt(2) == '/'; } // ==================== 供可视化窗口调用的公开 API ==================== diff --git a/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java b/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java index ba8cdb8..f4d4d63 100644 --- a/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java +++ b/src/main/java/com/example/workutils/biz/mybatisexport/MyBatisExportWindow.java @@ -3,6 +3,7 @@ package com.example.workutils.biz.mybatisexport; import com.example.workutils.biz.mybatisexport.service.MyBatisExportService; import com.example.workutils.system.config.WorkUtilsProperties; import com.example.workutils.system.support.HistoryPersistence; +import com.example.workutils.system.support.SystemOpenHelper; import com.example.workutils.system.ui.StyledButton; import com.example.workutils.system.ui.Theme; import javafx.application.Platform; @@ -17,7 +18,6 @@ import javafx.scene.layout.*; import javafx.scene.Parent; import javafx.stage.Stage; -import java.awt.Desktop; import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -242,7 +242,7 @@ public class MyBatisExportWindow { // 右键菜单选项 MenuItem defaultOpen = new MenuItem("系统默认程序打开"); defaultOpen.setOnAction(e -> openFile(currentFileName)); - MenuItem openNotepad = new MenuItem("用记事本打开"); + MenuItem openNotepad = new MenuItem("用系统编辑器打开"); openNotepad.setOnAction(e -> openWithNotepad(currentFileName)); MenuItem openFolder = new MenuItem("打开所在文件夹"); openFolder.setOnAction(e -> openFolder(currentFileName)); @@ -428,12 +428,7 @@ public class MyBatisExportWindow { try { File f = new File(path); if (!f.exists()) return; - String os = System.getProperty("os.name").toLowerCase(); - if (os.contains("win")) { - new ProcessBuilder("cmd", "/c", "start", "", path).start(); - } else { - Desktop.getDesktop().open(f); - } + SystemOpenHelper.openFile(f.toPath()); } catch (Exception ignored) { } } @@ -441,7 +436,7 @@ public class MyBatisExportWindow { private void openWithNotepad(String path) { if (path == null) return; try { - new ProcessBuilder("notepad.exe", path).start(); + SystemOpenHelper.editFile(path); } catch (Exception e) { openFile(path); } @@ -452,12 +447,7 @@ public class MyBatisExportWindow { try { File f = new File(path).getParentFile(); if (f == null || !f.exists()) return; - String os = System.getProperty("os.name").toLowerCase(); - if (os.contains("win")) { - new ProcessBuilder("explorer.exe", "/select,", path).start(); - } else { - Desktop.getDesktop().open(f); - } + SystemOpenHelper.revealInFileManager(path); } catch (Exception ignored) { } } diff --git a/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java b/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java index be4b265..58ca8a7 100644 --- a/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java +++ b/src/main/java/com/example/workutils/biz/scanner/ScannerWindow.java @@ -5,6 +5,7 @@ import com.example.workutils.system.config.WorkUtilsProperties; import com.example.workutils.system.support.FileOperationHelper; import com.example.workutils.system.support.HistoryPersistence; import com.example.workutils.system.support.OutputHelper; +import com.example.workutils.system.support.SystemOpenHelper; import com.example.workutils.system.ui.StyledButton; import com.example.workutils.system.ui.Theme; import javafx.application.Platform; @@ -19,7 +20,6 @@ import javafx.scene.layout.*; import javafx.scene.Parent; import javafx.stage.Stage; -import java.awt.Desktop; import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -222,7 +222,7 @@ public class ScannerWindow { // 右键菜单选项 MenuItem defaultOpen = new MenuItem("系统默认程序打开"); defaultOpen.setOnAction(e -> openFile(currentFileName)); - MenuItem openNotepad = new MenuItem("用记事本打开"); + MenuItem openNotepad = new MenuItem("用系统编辑器打开"); openNotepad.setOnAction(e -> openWithNotepad(currentFileName)); MenuItem openFolder = new MenuItem("打开所在文件夹"); openFolder.setOnAction(e -> openFolder(currentFileName)); @@ -372,13 +372,8 @@ public class ScannerWindow { logArea.appendText("[WARN] 文件不存在: " + path + "\n"); return; } - // Windows 用 cmd /c start 更可靠 - String os = System.getProperty("os.name").toLowerCase(); - if (os.contains("win")) { - new ProcessBuilder("cmd", "/c", "start", "", path).start(); - } else { - Desktop.getDesktop().open(f); - } + // Delegate platform-specific open behavior to SystemOpenHelper. + SystemOpenHelper.openFile(f.toPath()); logArea.appendText("[OK] 已打开: " + path + "\n"); } catch (Exception e) { logArea.appendText("[ERROR] 打开失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n"); @@ -387,8 +382,8 @@ public class ScannerWindow { private void openWithNotepad(String path) { try { - new ProcessBuilder("notepad.exe", path).start(); - logArea.appendText("[OK] 用记事本打开: " + path + "\n"); + SystemOpenHelper.editFile(path); + logArea.appendText("[OK] 用系统编辑器打开: " + path + "\n"); } catch (Exception e) { openFile(path); // fallback } @@ -405,12 +400,7 @@ public class ScannerWindow { logArea.appendText("[WARN] 目录不存在: " + path + "\n"); return; } - String os = System.getProperty("os.name").toLowerCase(); - if (os.contains("win")) { - new ProcessBuilder("explorer.exe", "/select,", path).start(); - } else { - Desktop.getDesktop().open(f); - } + SystemOpenHelper.revealInFileManager(path); logArea.appendText("[OK] 已打开目录: " + f.getAbsolutePath() + "\n"); } catch (Exception e) { logArea.appendText("[ERROR] 打开目录失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName()) + "\n"); diff --git a/src/main/java/com/example/workutils/system/config/SettingsWindow.java b/src/main/java/com/example/workutils/system/config/SettingsWindow.java index f8fc7fe..c6f658e 100644 --- a/src/main/java/com/example/workutils/system/config/SettingsWindow.java +++ b/src/main/java/com/example/workutils/system/config/SettingsWindow.java @@ -1,6 +1,7 @@ package com.example.workutils.system.config; import com.example.workutils.system.support.JfxBootstrap; +import com.example.workutils.system.support.SystemOpenHelper; import com.example.workutils.system.ui.Theme; import javafx.application.Platform; import javafx.geometry.Insets; @@ -18,7 +19,6 @@ import javafx.stage.Screen; import javafx.stage.Stage; import lombok.extern.slf4j.Slf4j; -import java.awt.Desktop; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -205,7 +205,7 @@ public class SettingsWindow { HBox btnRow = new HBox(10); btnRow.setAlignment(Pos.CENTER_LEFT); - Button btnOpen = new Button(isReadOnly ? "用记事本查看" : "用记事本打开"); + Button btnOpen = new Button(isReadOnly ? "用系统编辑器查看" : "用系统编辑器打开"); btnOpen.setFont(font(fontBtn)); btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY); allButtons.add(btnOpen); @@ -293,7 +293,7 @@ public class SettingsWindow { HBox btnRow = new HBox(10); btnRow.setAlignment(Pos.CENTER_LEFT); - Button btnOpen = new Button("用记事本打开"); + Button btnOpen = new Button("用系统编辑器打开"); btnOpen.setFont(font(fontBtn)); btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY); allButtons.add(btnOpen); @@ -421,7 +421,7 @@ public class SettingsWindow { HBox fileBtnRow = new HBox(10); fileBtnRow.setAlignment(Pos.CENTER_LEFT); - Button btnOpen = new Button("用记事本编辑"); + Button btnOpen = new Button("用系统编辑器编辑"); btnOpen.setFont(font(fontBtn)); btnOpen.getStyleClass().add(Theme.CSS_BTN_PRIMARY); allButtons.add(btnOpen); @@ -553,7 +553,7 @@ public class SettingsWindow { HBox btnRow = new HBox(10); btnRow.setAlignment(Pos.CENTER_LEFT); - Button btnView = new Button("用记事本查看"); + Button btnView = new Button("用系统编辑器查看"); btnView.setFont(font(fontBtn)); btnView.getStyleClass().add(Theme.CSS_BTN_PRIMARY); allButtons.add(btnView); @@ -986,11 +986,7 @@ public class SettingsWindow { Files.createFile(file); log.info("自动创建文件: {}", file.toAbsolutePath()); } - if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) { - Desktop.getDesktop().edit(file.toFile()); - } else { - Runtime.getRuntime().exec(new String[]{"notepad.exe", file.toAbsolutePath().toString()}); - } + SystemOpenHelper.editFile(file); } catch (IOException e) { log.error("打开文件失败: {}", file.toAbsolutePath(), e); showAlert(AlertType.ERROR, "打开文件失败: " + e.getMessage()); diff --git a/src/main/java/com/example/workutils/system/support/SystemOpenHelper.java b/src/main/java/com/example/workutils/system/support/SystemOpenHelper.java new file mode 100644 index 0000000..53be5b4 --- /dev/null +++ b/src/main/java/com/example/workutils/system/support/SystemOpenHelper.java @@ -0,0 +1,191 @@ +package com.example.workutils.system.support; + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Locale; + +public final class SystemOpenHelper { + + private static final OperatingSystem CURRENT_OS = detectOs(); + + private SystemOpenHelper() { + } + + public static void initialize() { + currentOs(); + } + + public static OperatingSystem currentOs() { + return CURRENT_OS; + } + + public static boolean isWindows() { + return CURRENT_OS == OperatingSystem.WINDOWS; + } + + public static boolean isMac() { + return CURRENT_OS == OperatingSystem.MAC; + } + + public static void openFile(String path) throws IOException { + if (path == null || path.trim().isEmpty()) { + return; + } + openFile(Path.of(path)); + } + + public static void openFile(Path path) throws IOException { + if (path == null) { + return; + } + File file = path.toFile(); + if (!file.exists()) { + return; + } + if (tryDesktopOpen(file)) { + return; + } + runOpenFallback(file); + } + + public static void editFile(String path) throws IOException { + if (path == null || path.trim().isEmpty()) { + return; + } + editFile(Path.of(path)); + } + + public static void editFile(Path path) throws IOException { + if (path == null) { + return; + } + File file = path.toFile(); + if (!file.exists()) { + return; + } + if (tryDesktopEdit(file)) { + return; + } + switch (CURRENT_OS) { + case WINDOWS: + start("notepad.exe", file.getAbsolutePath()); + break; + case MAC: + start("open", "-e", file.getAbsolutePath()); + break; + case LINUX: + case OTHER: + default: + runOpenFallback(file); + break; + } + } + + public static void revealInFileManager(String path) throws IOException { + if (path == null || path.trim().isEmpty()) { + return; + } + revealInFileManager(Path.of(path)); + } + + public static void revealInFileManager(Path path) throws IOException { + if (path == null) { + return; + } + File target = path.toFile(); + File parent = target.isDirectory() ? target : target.getParentFile(); + if (parent == null || !parent.exists()) { + return; + } + switch (CURRENT_OS) { + case WINDOWS: + if (target.exists() && target.isFile()) { + start("explorer.exe", "/select," + target.getAbsolutePath()); + } else { + start("explorer.exe", parent.getAbsolutePath()); + } + break; + case MAC: + if (target.exists()) { + start("open", "-R", target.getAbsolutePath()); + } else { + start("open", parent.getAbsolutePath()); + } + break; + case LINUX: + case OTHER: + default: + if (!tryDesktopOpen(parent)) { + start("xdg-open", parent.getAbsolutePath()); + } + break; + } + } + + private static OperatingSystem detectOs() { + String osName = System.getProperty("os.name", "").toLowerCase(Locale.ROOT); + if (osName.contains("win")) { + return OperatingSystem.WINDOWS; + } + if (osName.contains("mac") || osName.contains("darwin")) { + return OperatingSystem.MAC; + } + if (osName.contains("nux") || osName.contains("nix") || osName.contains("aix")) { + return OperatingSystem.LINUX; + } + return OperatingSystem.OTHER; + } + + private static boolean tryDesktopOpen(File file) { + try { + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) { + Desktop.getDesktop().open(file); + return true; + } + } catch (IOException | RuntimeException ignored) { + return false; + } + return false; + } + + private static boolean tryDesktopEdit(File file) { + try { + if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.EDIT)) { + Desktop.getDesktop().edit(file); + return true; + } + } catch (IOException | RuntimeException ignored) { + return false; + } + return false; + } + + private static void runOpenFallback(File file) throws IOException { + switch (CURRENT_OS) { + case WINDOWS: + start("cmd", "/c", "start", "", file.getAbsolutePath()); + break; + case MAC: + start("open", file.getAbsolutePath()); + break; + case LINUX: + case OTHER: + default: + start("xdg-open", file.getAbsolutePath()); + break; + } + } + + private static void start(String... command) throws IOException { + new ProcessBuilder(command).start(); + } + + public enum OperatingSystem { + WINDOWS, + MAC, + LINUX, + OTHER + } +}