From 931b964226ef271cc6d4f56dbbfe39f8a54f2412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Mon, 3 Aug 2026 14:53:15 +0800 Subject: [PATCH] Add unified application path resolver --- .../system/support/ApplicationPaths.java | 68 +++++++++++++++++++ .../system/support/ApplicationPathsTest.java | 64 +++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/main/java/com/example/workutils/system/support/ApplicationPaths.java create mode 100644 src/test/java/com/example/workutils/system/support/ApplicationPathsTest.java diff --git a/src/main/java/com/example/workutils/system/support/ApplicationPaths.java b/src/main/java/com/example/workutils/system/support/ApplicationPaths.java new file mode 100644 index 0000000..5b906cf --- /dev/null +++ b/src/main/java/com/example/workutils/system/support/ApplicationPaths.java @@ -0,0 +1,68 @@ +package com.example.workutils.system.support; + +import java.nio.file.Path; +import java.util.Arrays; + +/** Resolves every WorkUtils runtime path from one stable application home. */ +public final class ApplicationPaths { + + public static final String HOME_PROPERTY = "workutils.home"; + public static final String HOME_ENV = "WORKUTILS_HOME"; + + private final Path home; + + private ApplicationPaths(Path home) { + this.home = home.toAbsolutePath().normalize(); + } + + public static ApplicationPaths current() { + return resolve(System.getProperty(HOME_PROPERTY), System.getenv(HOME_ENV), + System.getProperty("user.dir")); + } + + public static ApplicationPaths of(Path home) { + if (home == null) { + throw new IllegalArgumentException("Application home must not be null"); + } + return new ApplicationPaths(home); + } + + static ApplicationPaths resolve(String property, String environment, String userDirectory) { + String selected = firstNonBlank(property, environment, userDirectory); + if (selected == null) { + throw new IllegalStateException("Application home is unavailable"); + } + return new ApplicationPaths(Path.of(selected.trim())); + } + + public Path home() { + return home; + } + + public Path resolve(String first, String... more) { + return home.resolve(Path.of(first, more)).normalize(); + } + + public Path resolveConfigured(String configured, String... fallbackSegments) { + Path value; + if (configured == null || configured.isBlank()) { + if (fallbackSegments.length == 0) { + throw new IllegalArgumentException("A fallback path is required"); + } + value = Path.of(fallbackSegments[0], + Arrays.copyOfRange(fallbackSegments, 1, fallbackSegments.length)); + } else { + value = Path.of(configured.trim()); + } + return (value.isAbsolute() ? value : home.resolve(value)).normalize(); + } + + private static String firstNonBlank(String... values) { + for (String value : values) { + if (value != null && !value.isBlank()) { + return value; + } + } + return null; + } +} diff --git a/src/test/java/com/example/workutils/system/support/ApplicationPathsTest.java b/src/test/java/com/example/workutils/system/support/ApplicationPathsTest.java new file mode 100644 index 0000000..76070a4 --- /dev/null +++ b/src/test/java/com/example/workutils/system/support/ApplicationPathsTest.java @@ -0,0 +1,64 @@ +package com.example.workutils.system.support; + +import org.junit.jupiter.api.Test; + +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ApplicationPathsTest { + + @Test + void systemPropertyWinsOverEnvironmentAndWorkingDirectory() { + ApplicationPaths paths = ApplicationPaths.resolve( + " build/系统 属性 ", "build/环境变量", "build/工作目录"); + + assertEquals(Path.of("build", "系统 属性").toAbsolutePath().normalize(), paths.home()); + } + + @Test + void environmentWinsWhenSystemPropertyIsBlank() { + ApplicationPaths paths = ApplicationPaths.resolve( + " ", "build/环境 变量", "build/工作目录"); + + assertEquals(Path.of("build", "环境 变量").toAbsolutePath().normalize(), paths.home()); + } + + @Test + void blankOverridesFallBackToWorkingDirectory() { + ApplicationPaths paths = ApplicationPaths.resolve(" ", "", "build/工作 目录"); + + assertEquals(Path.of("build", "工作 目录").toAbsolutePath().normalize(), paths.home()); + } + + @Test + void missingAllSourcesIsRejected() { + assertThrows(IllegalStateException.class, + () -> ApplicationPaths.resolve(null, " ", null)); + } + + @Test + void configuredRelativePathUsesApplicationHome() { + ApplicationPaths paths = ApplicationPaths.of(Path.of("build", "运行 根目录")); + + assertEquals(paths.home().resolve(Path.of("data", "plugins.config")), + paths.resolveConfigured("data/plugins.config")); + } + + @Test + void configuredAbsolutePathIsPreserved() { + ApplicationPaths paths = ApplicationPaths.of(Path.of("build", "运行 根目录")); + Path absolute = Path.of(System.getProperty("java.io.tmpdir"), "绝对 配置").toAbsolutePath().normalize(); + + assertEquals(absolute, paths.resolveConfigured(absolute.toString())); + } + + @Test + void fallbackSegmentsAreResolvedWithoutEmbeddedSeparators() { + ApplicationPaths paths = ApplicationPaths.of(Path.of("build", "运行 根目录")); + + assertEquals(paths.home().resolve(Path.of("data", "api_base_classes.json")), + paths.resolveConfigured(null, "data", "api_base_classes.json")); + } +}