diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 28564f0af..df95029cd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -47,6 +47,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.time.Instant; import java.util.*; @@ -92,11 +93,13 @@ public class HMCLGameRepository extends DefaultGameRepository { case ROOT_FOLDER: return super.getRunDirectory(id); case CUSTOM: - File dir = new File(getVersionSetting(id).getGameDir()); - if (!FileUtils.isValidPath(dir)) return getVersionRoot(id); - return dir; + try { + return Path.of(getVersionSetting(id).getGameDir()).toFile(); + } catch (InvalidPathException ignored) { + return getVersionRoot(id); + } default: - throw new Error(); + throw new AssertionError("Unreachable"); } } @@ -119,9 +122,11 @@ public class HMCLGameRepository extends DefaultGameRepository { }); try { - File file = new File(getBaseDirectory(), "launcher_profiles.json"); - if (!file.exists() && !versions.isEmpty()) - FileUtils.writeText(file, PROFILE); + Path file = getBaseDirectory().toPath().resolve("launcher_profiles.json"); + if (!Files.exists(file) && !versions.isEmpty()) { + Files.createDirectories(file.getParent()); + Files.writeString(file, PROFILE); + } } catch (IOException ex) { LOG.warning("Unable to create launcher_profiles.json, Forge/LiteLoader installer will not work.", ex); } @@ -132,14 +137,14 @@ public class HMCLGameRepository extends DefaultGameRepository { refreshVersionsAsync().start(); } - private void clean(File directory) throws IOException { - FileUtils.deleteDirectory(new File(directory, "crash-reports")); - FileUtils.deleteDirectory(new File(directory, "logs")); + private void clean(Path directory) throws IOException { + FileUtils.deleteDirectory(directory.resolve("crash-reports")); + FileUtils.deleteDirectory(directory.resolve("logs")); } public void clean(String id) throws IOException { - clean(getBaseDirectory()); - clean(getRunDirectory(id)); + clean(getBaseDirectory().toPath()); + clean(getRunDirectory(id).toPath()); } public void duplicateVersion(String srcId, String dstId, boolean copySaves) throws IOException { @@ -270,14 +275,14 @@ public class HMCLGameRepository extends DefaultGameRepository { } public void setVersionIconFile(String id, File iconFile) throws IOException { - String ext = FileUtils.getExtension(iconFile).toLowerCase(Locale.ROOT); + String ext = FileUtils.getExtension(iconFile.getName()).toLowerCase(Locale.ROOT); if (!FXUtils.IMAGE_EXTENSIONS.contains(ext)) { throw new IllegalArgumentException("Unsupported icon file: " + ext); } deleteIconFile(id); - FileUtils.copyFile(iconFile, new File(getVersionRoot(id), "icon." + ext)); + FileUtils.copyFile(iconFile.toPath(), getVersionRoot(id).toPath().resolve("icon." + ext)); } public void deleteIconFile(String id) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index ff8763ac7..19eec2725 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -81,7 +81,7 @@ public final class ModpackHelper { } public static boolean isFileModpackByExtension(File file) { - String ext = FileUtils.getExtension(file); + String ext = FileUtils.getExtension(file.getName()); return "zip".equals(ext) || "mrpack".equals(ext); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java index e1fa386a1..b0100219e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java @@ -207,7 +207,7 @@ public final class HMCLJavaRepository implements JavaRepository { public Task getUninstallJavaTask(Platform platform, String name) { return Task.runAsync(() -> { Files.deleteIfExists(getManifestFile(platform, name)); - FileUtils.deleteDirectory(getJavaDir(platform, name).toFile()); + FileUtils.deleteDirectory(getJavaDir(platform, name)); }); } @@ -220,7 +220,7 @@ public final class HMCLJavaRepository implements JavaRepository { if (relativized.getNameCount() > 1) { String name = relativized.getName(0).toString(); Files.deleteIfExists(getManifestFile(java.getPlatform(), name)); - FileUtils.deleteDirectory(getJavaDir(java.getPlatform(), name).toFile()); + FileUtils.deleteDirectory(getJavaDir(java.getPlatform(), name)); } }); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index 869596dee..023141251 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -460,8 +460,10 @@ public final class FXUtils { } public static void openFolder(File file) { - if (!FileUtils.makeDirectory(file)) { - LOG.error("Unable to make directory " + file); + try { + Files.createDirectories(file.toPath()); + } catch (IOException e) { + LOG.warning("Failed to create directory " + file); return; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index 7fe6c5ac4..e1ab75a25 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -114,7 +114,7 @@ public final class LocalModpackPage extends ModpackPage { if (!name.isPresent()) { // trim: https://github.com/HMCL-dev/HMCL/issues/962 - txtModpackName.setText(FileUtils.getNameWithoutExtension(selectedFile)); + txtModpackName.setText(FileUtils.getNameWithoutExtension(selectedFile.getName())); } Controllers.confirm(i18n("modpack.type.manual.warning"), i18n("install.modpack"), MessageDialogPane.MessageType.WARNING, diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java index af472d2b7..379345ee4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackFileSelectionPage.java @@ -87,8 +87,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard ModAdviser.ModSuggestion state = ModAdviser.ModSuggestion.SUGGESTED; if (basePath.length() > "minecraft/".length()) { state = adviser.advise(StringUtils.substringAfter(basePath, "minecraft/") + (file.isDirectory() ? "/" : ""), file.isDirectory()); - if (file.isFile() && Objects.equals(FileUtils.getNameWithoutExtension(file), version)) // Ignore .json, .jar - state = ModAdviser.ModSuggestion.HIDDEN; + if (file.isFile() && Objects.equals(FileUtils.getNameWithoutExtension(file.getName()), version)) state = ModAdviser.ModSuggestion.HIDDEN; if (file.isDirectory() && Objects.equals(file.getName(), version + "-natives")) // Ignore -natives state = ModAdviser.ModSuggestion.HIDDEN; if (state == ModAdviser.ModSuggestion.HIDDEN) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index f937da70d..692b1378e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -38,7 +38,6 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.IOUtils; import org.tukaani.xz.XZInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -254,6 +253,9 @@ public final class SettingsPage extends SettingsView { @Override protected void clearCacheDirectory() { - FileUtils.cleanDirectoryQuietly(new File(Settings.instance().getCommonDirectory(), "cache")); + String commonDirectory = Settings.instance().getCommonDirectory(); + if (commonDirectory != null) { + FileUtils.cleanDirectoryQuietly(Path.of(commonDirectory, "cache")); + } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPage.java index 82b76cf68..04f319fdd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPage.java @@ -50,7 +50,7 @@ public final class DatapackListPage extends ListPageBase Objects.equals("zip", FileUtils.getExtension(it)), + FXUtils.applyDragListener(this, it -> Objects.equals("zip", FileUtils.getExtension(it.getName())), mods -> mods.forEach(this::installSingleDatapack), this::refresh); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java index 9405e3800..2a6e4c040 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/InstallerListPage.java @@ -49,7 +49,7 @@ public class InstallerListPage extends ListPageBase implements Ve private String gameVersion; { - FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "exe").contains(FileUtils.getExtension(it)), mods -> { + FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "exe").contains(FileUtils.getExtension(it.getName())), mods -> { if (!mods.isEmpty()) doInstallOffline(mods.get(0)); }); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java index 5cc7e6e07..4e75a8b07 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPage.java @@ -59,7 +59,7 @@ public final class ModListPage extends ListPageBase Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it)), mods -> { + FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it.getName())), mods -> { mods.forEach(it -> { try { modManager.addMod(it.toPath()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java index c383773cb..3e9889b58 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/SchematicsPage.java @@ -373,7 +373,7 @@ public final class SchematicsPage extends ListPageBase impl @Override void onDelete() { try { - FileUtils.cleanDirectory(path.toFile()); + FileUtils.cleanDirectory(path); Files.deleteIfExists(path); refresh(); } catch (IOException e) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java index cc1dbe114..0cd79dfd8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java @@ -166,14 +166,14 @@ public class VersionPage extends DecoratorAnimatedPage implements DecoratorPage } private void clearLibraries() { - FileUtils.deleteDirectoryQuietly(new File(getProfile().getRepository().getBaseDirectory(), "libraries")); + FileUtils.deleteDirectoryQuietly(getProfile().getRepository().getBaseDirectory().toPath().resolve("libraries")); } private void clearAssets() { HMCLGameRepository baseDirectory = getProfile().getRepository(); - FileUtils.deleteDirectoryQuietly(new File(baseDirectory.getBaseDirectory(), "assets")); + FileUtils.deleteDirectoryQuietly(baseDirectory.getBaseDirectory().toPath().resolve("assets")); if (version.get() != null) { - FileUtils.deleteDirectoryQuietly(new File(baseDirectory.getRunDirectory(version.get().getVersion()), "resources")); + FileUtils.deleteDirectoryQuietly(baseDirectory.getRunDirectory(version.get().getVersion()).toPath().resolve("resources")); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java index 161107555..f5913b35c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListPage.java @@ -53,7 +53,7 @@ public final class WorldListPage extends ListPageBase implements private String gameVersion; public WorldListPage() { - FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it)), modpacks -> { + FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it.getName())), modpacks -> { installWorld(modpacks.get(0)); }); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java index b9b04dcfd..73e1b0bab 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java @@ -421,6 +421,6 @@ public class ForgeNewInstallTask extends Task { @Override public void postExecute() throws Exception { - FileUtils.deleteDirectory(tempDir.toFile()); + FileUtils.deleteDirectory(tempDir); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java index 6a595b967..8b91fd98b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeOldInstallTask.java @@ -24,10 +24,11 @@ import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.gson.JsonUtils; -import org.jackhuang.hmcl.util.io.FileUtils; import java.io.*; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; @@ -71,12 +72,12 @@ public class ForgeOldInstallTask extends Task { // unpack the universal jar in the installer file. Library forgeLibrary = new Library(installProfile.getInstall().getPath()); - File forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary); - if (!FileUtils.makeFile(forgeFile)) - throw new IOException("Cannot make directory " + forgeFile.getParent()); + Path forgeFile = dependencyManager.getGameRepository().getLibraryFile(version, forgeLibrary).toPath(); + Files.createDirectories(forgeFile.getParent()); ZipEntry forgeEntry = zipFile.getEntry(installProfile.getInstall().getFilePath()); - try (InputStream is = zipFile.getInputStream(forgeEntry); OutputStream os = new FileOutputStream(forgeFile)) { + try (InputStream is = zipFile.getInputStream(forgeEntry); + OutputStream os = Files.newOutputStream(forgeFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { is.transferTo(os); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java index 1bd25c60a..084e5e7be 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java @@ -103,7 +103,7 @@ public final class GameLibrariesTask extends Task { if (library.getChecksums() != null && !library.getChecksums().isEmpty() && !LibraryDownloadTask.checksumValid(file, library.getChecksums())) { return true; } - if (FileUtils.getExtension(file).equals("jar")) { + if (FileUtils.getExtension(file.getName()).equals("jar")) { try { FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(jar, jar); } catch (IOException ignored) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java index 4d851c0af..f6fafa174 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java @@ -105,7 +105,7 @@ public class LibraryDownloadTask extends Task { Optional libPath = cacheRepository.getLibrary(originalLibrary); if (libPath.isPresent()) { try { - FileUtils.copyFile(libPath.get().toFile(), jar); + FileUtils.copyFile(libPath.get(), jar.toPath()); cached = true; return; } catch (IOException e) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java index d47672486..eeb6b03b6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java @@ -417,6 +417,6 @@ public class NeoForgeOldInstallTask extends Task { @Override public void postExecute() throws Exception { - FileUtils.deleteDirectory(tempDir.toFile()); + FileUtils.deleteDirectory(tempDir); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java index d65d93dca..a4f68eb53 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java @@ -167,7 +167,7 @@ public final class OptiFineInstallTask extends Task { if (Files.exists(launchWrapper2)) { Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper", "2.0")); File launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper); - FileUtils.makeDirectory(launchWrapperFile.getAbsoluteFile().getParentFile()); + Files.createDirectories(launchWrapperFile.toPath().toAbsolutePath().getParent()); FileUtils.copyFile(launchWrapper2, launchWrapperFile.toPath()); hasLaunchWrapper = true; libraries.add(launchWrapper); @@ -182,7 +182,7 @@ public final class OptiFineInstallTask extends Task { if (Files.exists(launchWrapperJar)) { File launchWrapperFile = gameRepository.getLibraryFile(version, launchWrapper); - FileUtils.makeDirectory(launchWrapperFile.getAbsoluteFile().getParentFile()); + Files.createDirectories(launchWrapperFile.toPath().toAbsolutePath().getParent()); FileUtils.copyFile(launchWrapperJar, launchWrapperFile.toPath()); hasLaunchWrapper = true; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java index 57608468f..cdf3127c0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -214,7 +214,7 @@ public class DefaultGameRepository implements GameRepository { if (fromVersion.getId().equals(fromVersion.getJar())) fromVersion = fromVersion.setJar(null); - FileUtils.writeText(toJson, JsonUtils.GSON.toJson(fromVersion.setId(to))); + JsonUtils.writeToJsonFile(toJson, fromVersion.setId(to)); // fix inheritsFrom of versions that inherits from version [from]. for (Version version : getVersions()) { @@ -235,7 +235,7 @@ public class DefaultGameRepository implements GameRepository { if (EventBus.EVENT_BUS.fireEvent(new RemoveVersionEvent(this, id)) == Event.Result.DENY) return false; if (!versions.containsKey(id)) - return FileUtils.deleteDirectoryQuietly(getVersionRoot(id)); + return FileUtils.deleteDirectoryQuietly(getVersionRoot(id).toPath()); File file = getVersionRoot(id); if (!file.exists()) return true; @@ -247,19 +247,23 @@ public class DefaultGameRepository implements GameRepository { try { versions.remove(id); - if (FileUtils.moveToTrash(removedFile)) { + if (FileUtils.moveToTrash(removedFile.toPath())) { return true; } // remove json files first to ensure HMCL will not recognize this folder as a valid version. - List jsons = FileUtils.listFilesByExtension(removedFile, "json"); - jsons.forEach(f -> { - if (!f.delete()) - LOG.warning("Unable to delete file " + f); - }); + + for (Path path : FileUtils.listFilesByExtension(removedFile.toPath(), "json")) { + try { + Files.delete(path); + } catch (IOException e) { + LOG.warning("Failed to delete file " + path, e); + } + } + // remove the version from version list regardless of whether the directory was removed successfully or not. try { - FileUtils.deleteDirectory(removedFile); + FileUtils.deleteDirectory(removedFile.toPath()); } catch (IOException e) { LOG.warning("Unable to remove version folder: " + file, e); } @@ -288,10 +292,10 @@ public class DefaultGameRepository implements GameRepository { // If user renamed the json file by mistake or created the json file in a wrong name, // we will find the only json and rename it to correct name. if (!json.exists()) { - List jsons = FileUtils.listFilesByExtension(dir, "json"); + List jsons = FileUtils.listFilesByExtension(dir.toPath(), "json"); if (jsons.size() == 1) { LOG.info("Renaming json file " + jsons.get(0) + " to " + json); - if (!jsons.get(0).renameTo(json)) { + if (!jsons.get(0).toFile().renameTo(json)) { LOG.warning("Cannot rename json file, ignoring version " + id); return Stream.empty(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 621ff68c2..f1898243e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -378,7 +378,7 @@ public class DefaultLauncher extends Launcher { public void decompressNatives(File destination) throws NotDecompressingNativesException { try { - FileUtils.cleanDirectoryQuietly(destination); + FileUtils.cleanDirectoryQuietly(destination.toPath()); for (Library library : version.getLibraries()) if (library.isNative()) new Unzipper(repository.getLibraryFile(version, library), destination) @@ -596,7 +596,7 @@ public class DefaultLauncher extends Launcher { if (isUsingLog4j()) extractLog4jConfigurationFile(); - String scriptExtension = FileUtils.getExtension(scriptFile); + String scriptExtension = FileUtils.getExtension(scriptFile.getName()); boolean usePowerShell = "ps1".equals(scriptExtension); if (!usePowerShell) { @@ -616,8 +616,7 @@ public class DefaultLauncher extends Launcher { } } - if (!FileUtils.makeFile(scriptFile)) - throw new IOException("Script file: " + scriptFile + " cannot be created."); + Files.createDirectories(scriptFile.toPath().getParent()); try (OutputStream outputStream = Files.newOutputStream(scriptFile.toPath())) { Charset charset = StandardCharsets.UTF_8; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java index 21348556c..f50ef5c42 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java @@ -63,7 +63,7 @@ public class Datapack { try (DirectoryStream directoryStream = Files.newDirectoryStream(datapacks)) { for (Path datapack : directoryStream) { if (Files.isDirectory(datapack) && packs.contains(FileUtils.getName(datapack))) - FileUtils.deleteDirectory(datapack.toFile()); + FileUtils.deleteDirectory(datapack); else if (Files.isRegularFile(datapack) && packs.contains(FileUtils.getNameWithoutExtension(datapack))) Files.delete(datapack); } @@ -106,14 +106,14 @@ public class Datapack { Files.delete(packPng); } } else { - FileUtils.copyFile(path.toFile(), datapacks.resolve(FileUtils.getName(path)).toFile()); + FileUtils.copyFile(path, datapacks.resolve(FileUtils.getName(path))); } } public void deletePack(Pack pack) throws IOException { Path subPath = pack.file; if (Files.isDirectory(subPath)) - FileUtils.deleteDirectory(subPath.toFile()); + FileUtils.deleteDirectory(subPath); else if (Files.isRegularFile(subPath)) Files.delete(subPath); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java index efc0ffa52..839b93c5b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModpackInstallTask.java @@ -19,11 +19,9 @@ package org.jackhuang.hmcl.mod; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; -import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.Unzipper; import java.io.File; -import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.*; @@ -63,8 +61,7 @@ public class ModpackInstallTask extends Task { @Override public void execute() throws Exception { Set entries = new HashSet<>(); - if (!FileUtils.makeDirectory(dest)) - throw new IOException("Unable to make directory " + dest); + Files.createDirectories(dest.toPath()); HashMap files = new HashMap<>(); for (ModpackConfiguration.FileInformation file : overrides) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java index 61ef87b73..cb5fb63b8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java @@ -17,16 +17,19 @@ */ package org.jackhuang.hmcl.util.io; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.glavo.chardet.DetectedCharset; import org.glavo.chardet.UniversalDetector; -import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.time.ZonedDateTime; @@ -34,6 +37,7 @@ import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.function.Predicate; +import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.UTF_8; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -81,16 +85,12 @@ public final class FileUtils { return StringUtils.substringBeforeLast(fileName, '.'); } - public static String getNameWithoutExtension(File file) { - return StringUtils.substringBeforeLast(file.getName(), '.'); - } - public static String getNameWithoutExtension(Path file) { return StringUtils.substringBeforeLast(getName(file), '.'); } - public static String getExtension(File file) { - return StringUtils.substringAfterLast(file.getName(), '.'); + public static String getExtension(String fileName) { + return StringUtils.substringAfterLast(fileName, '.'); } public static String getExtension(Path file) { @@ -105,13 +105,8 @@ public final class FileUtils { } public static String getName(Path path) { - if (path.getFileName() == null) return ""; - return StringUtils.removeSuffix(path.getFileName().toString(), "/", "\\"); - } - - public static String getName(Path path, String candidate) { - if (path.getFileName() == null) return candidate; - else return getName(path); + Path fileName = path.getFileName(); + return fileName != null ? fileName.toString() : ""; } // https://learn.microsoft.com/biztalk/core/restrictions-when-configuring-the-file-adapter @@ -210,21 +205,6 @@ public final class FileUtils { return new String(bytes, OperatingSystem.NATIVE_CHARSET); } - /** - * Write plain text to file. Characters are encoded into bytes using UTF-8. - *

- * We don't care about platform difference of line separator. Because readText accept all possibilities of line separator. - * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. - * All characters in text will be written into the file in binary format. Existing data will be erased. - * - * @param file the path to the file - * @param text the text being written to file - * @throws IOException if an I/O error occurs - */ - public static void writeText(File file, String text) throws IOException { - writeText(file.toPath(), text); - } - /** * Write plain text to file. Characters are encoded into bytes using UTF-8. *

@@ -241,19 +221,6 @@ public final class FileUtils { Files.writeString(file, text); } - /** - * Write byte array to file. - * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. - * All bytes in byte array will be written into the file in binary format. Existing data will be erased. - * - * @param file the path to the file - * @param data the data being written to file - * @throws IOException if an I/O error occurs - */ - public static void writeBytes(File file, byte[] data) throws IOException { - writeBytes(file.toPath(), data); - } - /** * Write byte array to file. * It will create the file if it does not exist, or truncate the existing file to empty for rewriting. @@ -278,22 +245,7 @@ public final class FileUtils { Files.deleteIfExists(directory); } - public static void deleteDirectory(File directory) - throws IOException { - if (!directory.exists()) - return; - - if (!isSymlink(directory)) - cleanDirectory(directory); - - if (!directory.delete()) { - String message = "Unable to delete directory " + directory + "."; - - throw new IOException(message); - } - } - - public static boolean deleteDirectoryQuietly(File directory) { + public static boolean deleteDirectoryQuietly(Path directory) { try { deleteDirectory(directory); return true; @@ -315,7 +267,7 @@ public final class FileUtils { } public static void copyDirectory(Path src, Path dest, Predicate filePredicate) throws IOException { - Files.walkFileTree(src, new SimpleFileVisitor() { + Files.walkFileTree(src, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (!filePredicate.test(src.relativize(file).toString())) { @@ -373,9 +325,9 @@ public final class FileUtils { * @param file the file being moved to trash. * @return false if moveToTrash does not exist, or platform does not support Desktop.Action.MOVE_TO_TRASH */ - public static boolean moveToTrash(File file) { + public static boolean moveToTrash(Path file) { if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && hasKnownDesktop()) { - if (!file.exists()) { + if (!Files.exists(file)) { return false; } @@ -395,7 +347,7 @@ public final class FileUtils { Files.createDirectories(infoDir); Files.createDirectories(filesDir); - String name = file.getName(); + String name = getName(file); Path infoFile = infoDir.resolve(name + ".trashinfo"); Path targetFile = filesDir.resolve(name); @@ -408,13 +360,13 @@ public final class FileUtils { } String time = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(ZonedDateTime.now().truncatedTo(ChronoUnit.SECONDS)); - if (file.isDirectory()) { - FileUtils.copyDirectory(file.toPath(), targetFile); + if (Files.isDirectory(file)) { + FileUtils.copyDirectory(file, targetFile); } else { - FileUtils.copyFile(file.toPath(), targetFile); + FileUtils.copyFile(file, targetFile); } - FileUtils.writeText(infoFile, "[Trash Info]\nPath=" + file.getAbsolutePath() + "\nDeletionDate=" + time + "\n"); + FileUtils.writeText(infoFile, "[Trash Info]\nPath=" + file.toAbsolutePath().normalize() + "\nDeletionDate=" + time + "\n"); FileUtils.forceDelete(file); } catch (IOException e) { LOG.warning("Failed to move " + file + " to trash", e); @@ -425,7 +377,7 @@ public final class FileUtils { } try { - return java.awt.Desktop.getDesktop().moveToTrash(file); + return java.awt.Desktop.getDesktop().moveToTrash(file.toFile()); } catch (Exception e) { return false; } @@ -460,36 +412,8 @@ public final class FileUtils { }); } - public static void cleanDirectory(File directory) - throws IOException { - if (!directory.exists()) { - if (!makeDirectory(directory)) - throw new IOException("Failed to create directory: " + directory); - return; - } - - if (!directory.isDirectory()) { - String message = directory + " is not a directory"; - throw new IllegalArgumentException(message); - } - - File[] files = directory.listFiles(); - if (files == null) - throw new IOException("Failed to list contents of " + directory); - - IOException exception = null; - for (File file : files) - try { - forceDelete(file); - } catch (IOException ioe) { - exception = ioe; - } - - if (null != exception) - throw exception; - } - - public static boolean cleanDirectoryQuietly(File directory) { + @CanIgnoreReturnValue + public static boolean cleanDirectoryQuietly(Path directory) { try { cleanDirectory(directory); return true; @@ -498,53 +422,12 @@ public final class FileUtils { } } - public static void forceDelete(File file) + public static void forceDelete(Path file) throws IOException { - if (file.isDirectory()) { + if (Files.isDirectory(file)) deleteDirectory(file); - } else { - boolean filePresent = file.exists(); - if (!file.delete()) { - if (!filePresent) - throw new FileNotFoundException("File does not exist: " + file); - throw new IOException("Unable to delete file: " + file); - } - } - } - - public static boolean isSymlink(File file) - throws IOException { - Objects.requireNonNull(file, "File must not be null"); - if (File.separatorChar == '\\') - return false; - File fileInCanonicalDir; - if (file.getParent() == null) - fileInCanonicalDir = file; - else { - File canonicalDir = file.getParentFile().getCanonicalFile(); - fileInCanonicalDir = new File(canonicalDir, file.getName()); - } - - return !fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()); - } - - public static void copyFile(File srcFile, File destFile) - throws IOException { - Objects.requireNonNull(srcFile, "Source must not be null"); - Objects.requireNonNull(destFile, "Destination must not be null"); - if (!srcFile.exists()) - throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); - if (srcFile.isDirectory()) - throw new IOException("Source '" + srcFile + "' exists but is a directory"); - if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) - throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); - File parentFile = destFile.getParentFile(); - if (parentFile != null && !FileUtils.makeDirectory(parentFile)) - throw new IOException("Destination '" + parentFile + "' directory cannot be created"); - if (destFile.exists() && !destFile.canWrite()) - throw new IOException("Destination '" + destFile + "' exists but is read-only"); - - Files.copy(srcFile.toPath(), destFile.toPath(), StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); + else + Files.delete(file); } public static void copyFile(Path srcFile, Path destFile) @@ -555,50 +438,20 @@ public final class FileUtils { throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); if (Files.isDirectory(srcFile)) throw new IOException("Source '" + srcFile + "' exists but is a directory"); - Path parentFile = destFile.getParent(); - Files.createDirectories(parentFile); + Files.createDirectories(destFile.getParent()); if (Files.exists(destFile) && !Files.isWritable(destFile)) throw new IOException("Destination '" + destFile + "' exists but is read-only"); Files.copy(srcFile, destFile, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING); } - public static void moveFile(File srcFile, File destFile) throws IOException { - copyFile(srcFile, destFile); - srcFile.delete(); - } - - public static boolean makeDirectory(File directory) { - directory.mkdirs(); - return directory.isDirectory(); - } - - public static boolean makeFile(File file) { - return makeDirectory(file.getAbsoluteFile().getParentFile()) && (file.exists() || Lang.test(file::createNewFile)); - } - - public static List listFilesByExtension(File file, String extension) { - List result = new ArrayList<>(); - File[] files = file.listFiles(); - if (files != null) - for (File it : files) - if (extension.equals(getExtension(it))) - result.add(it); - return result; - } - - /** - * Tests whether the file is convertible to [java.nio.file.Path] or not. - * - * @param file the file to be tested - * @return true if the file is convertible to Path. - */ - public static boolean isValidPath(File file) { - try { - file.toPath(); - return true; - } catch (InvalidPathException ignored) { - return false; + public static List listFilesByExtension(Path file, String extension) { + try (Stream list = Files.list(file)) { + return list.filter(it -> Files.isRegularFile(it) && extension.equals(getExtension(it))) + .toList(); + } catch (IOException e) { + LOG.warning("Failed to list files by extension " + extension, e); + return List.of(); } }