Add unified application path resolver

This commit is contained in:
张家训
2026-08-03 14:53:15 +08:00
parent f1c8232480
commit 931b964226
2 changed files with 132 additions and 0 deletions
@@ -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;
}
}
@@ -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"));
}
}