From 77fadf5d284116d747a65c4e9e6015aa75f634ec Mon Sep 17 00:00:00 2001 From: Glavo Date: Wed, 17 Sep 2025 21:14:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=20org.jackhuang.hmcl.ui=20=E4=BB=8E?= =?UTF-8?q?=20java.io.File=20=E8=BF=81=E7=A7=BB=E8=87=B3=20NIO=20(#4501)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/game/ModpackHelper.java | 4 +- .../org/jackhuang/hmcl/ui/Controllers.java | 4 +- .../java/org/jackhuang/hmcl/ui/FXUtils.java | 21 +++++---- .../hmcl/ui/account/AccountListItem.java | 14 +++--- .../ui/account/OfflineAccountSkinPane.java | 11 +++-- .../jackhuang/hmcl/ui/construct/FileItem.java | 31 +++++++----- .../hmcl/ui/construct/FileSelector.java | 11 +++-- .../hmcl/ui/download/LocalModpackPage.java | 20 ++++---- .../ModpackInstallWizardProvider.java | 19 ++++---- .../ui/download/ModpackSelectionPage.java | 10 ++-- .../hmcl/ui/export/ExportWizardProvider.java | 47 +++++++++---------- .../ui/export/ModpackFileSelectionPage.java | 34 +++++++++----- .../hmcl/ui/export/ModpackInfoPage.java | 5 +- .../hmcl/ui/main/JavaDownloadDialog.java | 11 +++-- .../hmcl/ui/main/JavaManagementPage.java | 17 +++---- .../org/jackhuang/hmcl/ui/main/RootPage.java | 22 ++++----- .../hmcl/ui/versions/DatapackListPage.java | 9 ++-- .../hmcl/ui/versions/DownloadPage.java | 7 +-- .../hmcl/ui/versions/InstallerListPage.java | 10 ++-- .../hmcl/ui/versions/ModListPage.java | 16 +++---- .../hmcl/ui/versions/SchematicsPage.java | 10 ++-- .../hmcl/ui/versions/VersionIconDialog.java | 7 +-- .../jackhuang/hmcl/ui/versions/Versions.java | 9 ++-- .../hmcl/ui/versions/WorldListItem.java | 6 +-- .../hmcl/ui/versions/WorldListPage.java | 9 ++-- .../org/jackhuang/hmcl/util/io/FileUtils.java | 27 +++++++++-- 26 files changed, 214 insertions(+), 177 deletions(-) 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 1d1be0878..a33e01f93 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -80,8 +80,8 @@ public final class ModpackHelper { return providers.get(type); } - public static boolean isFileModpackByExtension(File file) { - String ext = FileUtils.getExtension(file.getName()); + public static boolean isFileModpackByExtension(Path file) { + String ext = FileUtils.getExtension(file); return "zip".equals(ext) || "mrpack".equals(ext); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index d0e379b80..8a0b682a0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -62,7 +62,7 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.OperatingSystem; -import java.io.File; +import java.nio.file.Path; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -92,7 +92,7 @@ public final class Controllers { gameListPage.selectedProfileProperty().bindBidirectional(Profiles.selectedProfileProperty()); gameListPage.profilesProperty().bindContent(Profiles.profilesProperty()); FXUtils.applyDragListener(gameListPage, ModpackHelper::isFileModpackByExtension, modpacks -> { - File modpack = modpacks.get(0); + Path modpack = modpacks.get(0); Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(Profiles.getSelectedProfile(), modpack), i18n("install.modpack")); }); return gameListPage; 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 023141251..c56fd334f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -49,9 +49,7 @@ import javafx.scene.paint.Paint; import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; -import javafx.stage.FileChooser; -import javafx.stage.Screen; -import javafx.stage.Stage; +import javafx.stage.*; import javafx.util.Callback; import javafx.util.Duration; import javafx.util.StringConverter; @@ -87,6 +85,7 @@ import java.lang.ref.WeakReference; import java.net.*; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.PathMatcher; import java.util.List; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -96,7 +95,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import static org.jackhuang.hmcl.util.Lang.thread; import static org.jackhuang.hmcl.util.Lang.tryCast; @@ -1255,14 +1253,14 @@ public final class FXUtils { } } - public static void applyDragListener(Node node, FileFilter filter, Consumer> callback) { + public static void applyDragListener(Node node, PathMatcher filter, Consumer> callback) { applyDragListener(node, filter, callback, null); } - public static void applyDragListener(Node node, FileFilter filter, Consumer> callback, Runnable dragDropped) { + public static void applyDragListener(Node node, PathMatcher filter, Consumer> callback, Runnable dragDropped) { node.setOnDragOver(event -> { if (event.getGestureSource() != node && event.getDragboard().hasFiles()) { - if (event.getDragboard().getFiles().stream().anyMatch(filter::accept)) + if (event.getDragboard().getFiles().stream().map(File::toPath).anyMatch(filter::matches)) event.acceptTransferModes(TransferMode.COPY_OR_MOVE); } event.consume(); @@ -1271,7 +1269,7 @@ public final class FXUtils { node.setOnDragDropped(event -> { List files = event.getDragboard().getFiles(); if (files != null) { - List acceptFiles = files.stream().filter(filter::accept).collect(Collectors.toList()); + List acceptFiles = files.stream().map(File::toPath).filter(filter::matches).toList(); if (!acceptFiles.isEmpty()) { callback.accept(acceptFiles); event.setDropCompleted(true); @@ -1451,6 +1449,13 @@ public final class FXUtils { return String.format("#%02x%02x%02x", r, g, b); } + public static @Nullable List showOpenMultipleDialog(FileChooser chooser, Window ownerWindow) { + List files = chooser.showOpenMultipleDialog(ownerWindow); + if (files == null) + return null; + return files.stream().map(File::toPath).toList(); + } + public static FileChooser.ExtensionFilter getImageExtensionFilter() { return new FileChooser.ExtensionFilter(i18n("extension.png"), IMAGE_EXTENSIONS.stream().map(ext -> "*." + ext).toArray(String[]::new)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java index 6fc255dd9..65bbabb6a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountListItem.java @@ -41,13 +41,14 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.DialogController; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.skin.InvalidSkinException; import org.jackhuang.hmcl.util.skin.NormalizedSkin; import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Optional; import java.util.Set; import java.util.concurrent.CancellationException; @@ -116,8 +117,7 @@ public class AccountListItem extends RadioButton { } public ObservableBooleanValue canUploadSkin() { - if (account instanceof AuthlibInjectorAccount) { - AuthlibInjectorAccount aiAccount = (AuthlibInjectorAccount) account; + if (account instanceof AuthlibInjectorAccount aiAccount) { ObjectBinding> profile = aiAccount.getYggdrasilService().getProfileRepository().binding(aiAccount.getUUID()); return createBooleanBinding(() -> { Set uploadableTextures = profile.get() @@ -148,7 +148,7 @@ public class AccountListItem extends RadioButton { FileChooser chooser = new FileChooser(); chooser.setTitle(i18n("account.skin.upload")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("account.skin.file"), "*.png")); - File selectedFile = chooser.showOpenDialog(Controllers.getStage()); + Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile == null) { return null; } @@ -156,7 +156,7 @@ public class AccountListItem extends RadioButton { return refreshAsync() .thenRunAsync(() -> { Image skinImg; - try (FileInputStream input = new FileInputStream(selectedFile)) { + try (var input = Files.newInputStream(selectedFile)) { skinImg = new Image(input); } catch (IOException e) { throw new InvalidSkinException("Failed to read skin image", e); @@ -167,7 +167,7 @@ public class AccountListItem extends RadioButton { NormalizedSkin skin = new NormalizedSkin(skinImg); String model = skin.isSlim() ? "slim" : ""; LOG.info("Uploading skin [" + selectedFile + "], model [" + model + "]"); - account.uploadSkin(skin.isSlim(), selectedFile.toPath()); + account.uploadSkin(skin.isSlim(), selectedFile); }) .thenComposeAsync(refreshAsync()) .whenComplete(Schedulers.javafx(), e -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/OfflineAccountSkinPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/OfflineAccountSkinPane.java index e2e14701e..ce6bc7ce3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/OfflineAccountSkinPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/OfflineAccountSkinPane.java @@ -39,8 +39,9 @@ import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.*; +import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; +import java.nio.file.Path; import java.util.Arrays; import java.util.UUID; @@ -81,16 +82,16 @@ public class OfflineAccountSkinPane extends StackPane { canvas.addEventHandler(DragEvent.DRAG_OVER, e -> { if (e.getDragboard().hasFiles()) { - File file = e.getDragboard().getFiles().get(0); - if (file.getAbsolutePath().endsWith(".png")) + Path file = e.getDragboard().getFiles().get(0).toPath(); + if (FileUtils.getName(file).endsWith(".png")) e.acceptTransferModes(TransferMode.COPY); } }); canvas.addEventHandler(DragEvent.DRAG_DROPPED, e -> { if (e.isAccepted()) { - File skin = e.getDragboard().getFiles().get(0); + Path skin = e.getDragboard().getFiles().get(0).toPath(); Platform.runLater(() -> { - skinSelector.setValue(skin.getAbsolutePath()); + skinSelector.setValue(FileUtils.getAbsolutePath(skin)); skinItem.setSelectedData(Skin.Type.LOCAL_FILE); }); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileItem.java index d49df50e5..ccf722f3b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileItem.java @@ -27,17 +27,19 @@ import javafx.scene.control.Tooltip; import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import javafx.stage.DirectoryChooser; +import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; -import java.nio.file.Paths; import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; public class FileItem extends BorderPane { private final Label lblPath = new Label(); @@ -77,14 +79,14 @@ public class FileItem extends BorderPane { private String processPath(String path) { Path given; try { - given = Paths.get(path).toAbsolutePath(); + given = Path.of(path).toAbsolutePath().normalize(); } catch (IllegalArgumentException e) { return path; } if (isConvertToRelativePath()) { try { - return Paths.get(".").normalize().toAbsolutePath().relativize(given).normalize().toString(); + return Metadata.CURRENT_DIRECTORY.relativize(given).normalize().toString(); } catch (IllegalArgumentException e) { // the given path can't be relativized against current path } @@ -95,17 +97,22 @@ public class FileItem extends BorderPane { public void onExplore() { DirectoryChooser chooser = new DirectoryChooser(); if (path.get() != null) { - File file = new File(path.get()); - if (file.exists()) { - if (file.isFile()) - file = file.getAbsoluteFile().getParentFile(); - else if (file.isDirectory()) - file = file.getAbsoluteFile(); - chooser.setInitialDirectory(file); + Path file; + try { + file = Path.of(path.get()); + if (Files.exists(file)) { + if (Files.isRegularFile(file)) + file = file.toAbsolutePath().normalize().getParent(); + else if (Files.isDirectory(file)) + file = file.toAbsolutePath().normalize(); + chooser.setInitialDirectory(file.toFile()); + } + } catch (InvalidPathException e) { + LOG.warning("Failed to resolve path: " + path.get()); } } chooser.titleProperty().bind(titleProperty()); - File selectedDir = chooser.showDialog(Controllers.getStage()); + var selectedDir = chooser.showDialog(Controllers.getStage()); if (selectedDir != null) { path.set(processPath(selectedDir.toString())); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileSelector.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileSelector.java index 450071348..96cffb9a0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileSelector.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FileSelector.java @@ -31,8 +31,9 @@ import org.jackhuang.hmcl.setting.Theme; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; +import java.nio.file.Path; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -86,9 +87,9 @@ public class FileSelector extends HBox { if (directory) { DirectoryChooser chooser = new DirectoryChooser(); chooser.setTitle(chooserTitle); - File dir = chooser.showDialog(Controllers.getStage()); + Path dir = FileUtils.toPath(chooser.showDialog(Controllers.getStage())); if (dir != null) { - String path = dir.getAbsolutePath(); + String path = FileUtils.getAbsolutePath(dir); customField.setText(path); value.setValue(path); } @@ -96,9 +97,9 @@ public class FileSelector extends HBox { FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().addAll(getExtensionFilters()); chooser.setTitle(chooserTitle); - File file = chooser.showOpenDialog(Controllers.getStage()); + Path file = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (file != null) { - String path = file.getAbsolutePath(); + String path = FileUtils.getAbsolutePath(file); customField.setText(path); value.setValue(path); } 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 e1ab75a25..ee684ba1b 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 @@ -40,8 +40,8 @@ import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; import java.nio.charset.Charset; +import java.nio.file.Path; import java.util.Map; import java.util.Optional; @@ -82,15 +82,15 @@ public final class LocalModpackPage extends ModpackPage { btnDescription.setVisible(false); - File selectedFile; - Optional filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class); + Path selectedFile; + Optional filePath = tryCast(controller.getSettings().get(MODPACK_FILE), Path.class); if (filePath.isPresent()) { selectedFile = filePath.get(); } else { FileChooser chooser = new FileChooser(); chooser.setTitle(i18n("modpack.choose")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); - selectedFile = chooser.showOpenDialog(Controllers.getStage()); + selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile == null) { controller.onEnd(); return; @@ -100,21 +100,21 @@ public final class LocalModpackPage extends ModpackPage { } showSpinner(); - Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath())) + Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(selectedFile)) .thenApplyAsync(encoding -> { charset = encoding; - manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding); + manifest = ModpackHelper.readModpackManifest(selectedFile, encoding); return manifest; }) .whenComplete(Schedulers.javafx(), (manifest, exception) -> { if (exception instanceof ManuallyCreatedModpackException) { hideSpinner(); - lblName.setText(selectedFile.getName()); + lblName.setText(FileUtils.getName(selectedFile)); installAsVersion.set(false); - if (!name.isPresent()) { + if (name.isEmpty()) { // trim: https://github.com/HMCL-dev/HMCL/issues/962 - txtModpackName.setText(FileUtils.getNameWithoutExtension(selectedFile.getName())); + txtModpackName.setText(FileUtils.getNameWithoutExtension(selectedFile)); } Controllers.confirm(i18n("modpack.type.manual.warning"), i18n("install.modpack"), MessageDialogPane.MessageType.WARNING, @@ -133,7 +133,7 @@ public final class LocalModpackPage extends ModpackPage { lblVersion.setText(manifest.getVersion()); lblAuthor.setText(manifest.getAuthor()); - if (!name.isPresent()) { + if (name.isEmpty()) { // trim: https://github.com/HMCL-dev/HMCL/issues/962 txtModpackName.setText(manifest.getName().trim()); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index c7c42b1b2..3564180cd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -32,26 +32,27 @@ import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardProvider; +import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.file.Path; import java.util.Map; import static org.jackhuang.hmcl.util.Lang.tryCast; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; -public class ModpackInstallWizardProvider implements WizardProvider { +public final class ModpackInstallWizardProvider implements WizardProvider { private final Profile profile; - private final File file; + private final Path file; private final String updateVersion; public ModpackInstallWizardProvider(Profile profile) { this(profile, null, null); } - public ModpackInstallWizardProvider(Profile profile, File modpackFile) { + public ModpackInstallWizardProvider(Profile profile, Path modpackFile) { this(profile, modpackFile, null); } @@ -59,7 +60,7 @@ public class ModpackInstallWizardProvider implements WizardProvider { this(profile, null, updateVersion); } - public ModpackInstallWizardProvider(Profile profile, File modpackFile, String updateVersion) { + public ModpackInstallWizardProvider(Profile profile, Path modpackFile, String updateVersion) { this.profile = profile; this.file = modpackFile; this.updateVersion = updateVersion; @@ -75,7 +76,7 @@ public class ModpackInstallWizardProvider implements WizardProvider { } private Task finishModpackInstallingAsync(Map settings) { - File selected = tryCast(settings.get(LocalModpackPage.MODPACK_FILE), File.class).orElse(null); + Path selected = tryCast(settings.get(LocalModpackPage.MODPACK_FILE), Path.class).orElse(null); ServerModpackManifest serverModpackManifest = tryCast(settings.get(RemoteModpackPage.MODPACK_SERVER_MANIFEST), ServerModpackManifest.class).orElse(null); Modpack modpack = tryCast(settings.get(LocalModpackPage.MODPACK_MANIFEST), Modpack.class).orElse(null); String name = tryCast(settings.get(LocalModpackPage.MODPACK_NAME), String.class).orElse(null); @@ -83,7 +84,7 @@ public class ModpackInstallWizardProvider implements WizardProvider { boolean isManuallyCreated = tryCast(settings.get(LocalModpackPage.MODPACK_MANUALLY_CREATED), Boolean.class).orElse(false); if (isManuallyCreated) { - return ModpackHelper.getInstallManuallyCreatedModpackTask(profile, selected, name, charset); + return ModpackHelper.getInstallManuallyCreatedModpackTask(profile, FileUtils.toFile(selected), name, charset); } if ((selected == null && serverModpackManifest == null) || modpack == null || name == null) return null; @@ -97,7 +98,7 @@ public class ModpackInstallWizardProvider implements WizardProvider { if (serverModpackManifest != null) { return ModpackHelper.getUpdateTask(profile, serverModpackManifest, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name).toFile())); } else { - return ModpackHelper.getUpdateTask(profile, selected, modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name).toFile())); + return ModpackHelper.getUpdateTask(profile, selected.toFile(), modpack.getEncoding(), name, ModpackHelper.readModpackConfiguration(profile.getRepository().getModpackConfiguration(name).toFile())); } } catch (UnsupportedModpackException | ManuallyCreatedModpackException e) { Controllers.dialog(i18n("modpack.unsupported"), i18n("message.error"), MessageType.ERROR); @@ -112,7 +113,7 @@ public class ModpackInstallWizardProvider implements WizardProvider { return ModpackHelper.getInstallTask(profile, serverModpackManifest, name, modpack) .thenRunAsync(Schedulers.javafx(), () -> profile.setSelectedVersion(name)); } else { - return ModpackHelper.getInstallTask(profile, selected, name, modpack) + return ModpackHelper.getInstallTask(profile, selected.toFile(), name, modpack) .thenRunAsync(Schedulers.javafx(), () -> profile.setSelectedVersion(name)); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java index a1bcd165f..433260393 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackSelectionPage.java @@ -38,8 +38,8 @@ import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -71,14 +71,14 @@ public final class ModpackSelectionPage extends VBox implements WizardPage { createButton("repository", this::onChooseRepository) ); - Optional filePath = tryCast(controller.getSettings().get(MODPACK_FILE), File.class); + Optional filePath = tryCast(controller.getSettings().get(MODPACK_FILE), Path.class); if (filePath.isPresent()) { controller.getSettings().put(MODPACK_FILE, filePath.get()); Platform.runLater(controller::onNext); } FXUtils.applyDragListener(this, ModpackHelper::isFileModpackByExtension, modpacks -> { - File modpack = modpacks.get(0); + Path modpack = modpacks.get(0); controller.getSettings().put(MODPACK_FILE, modpack); controller.onNext(); }); @@ -112,7 +112,7 @@ public final class ModpackSelectionPage extends VBox implements WizardPage { FileChooser chooser = new FileChooser(); chooser.setTitle(i18n("modpack.choose")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip", "*.mrpack")); - File selectedFile = chooser.showOpenDialog(Controllers.getStage()); + Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile == null) { Platform.runLater(controller::onEnd); return; @@ -150,7 +150,7 @@ public final class ModpackSelectionPage extends VBox implements WizardPage { .whenComplete(Schedulers.javafx(), e -> { if (e == null) { resolve.run(); - controller.getSettings().put(MODPACK_FILE, modpack.toFile()); + controller.getSettings().put(MODPACK_FILE, modpack); controller.onNext(); } else { reject.accept(e.getMessage()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index 247527cf1..e061c795f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -38,7 +38,6 @@ import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.io.Zipper; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -62,7 +61,7 @@ public final class ExportWizardProvider implements WizardProvider { public Object finish(Map settings) { @SuppressWarnings("unchecked") List whitelist = (List) settings.get(ModpackFileSelectionPage.MODPACK_FILE_SELECTION); - File modpackFile = (File) settings.get(ModpackInfoPage.MODPACK_FILE); + Path modpackFile = (Path) settings.get(ModpackInfoPage.MODPACK_FILE); ModpackExportInfo exportInfo = (ModpackExportInfo) settings.get(ModpackInfoPage.MODPACK_INFO); exportInfo.setWhitelist(whitelist); String modpackType = (String) settings.get(ModpackTypeSelectionPage.MODPACK_TYPE); @@ -70,11 +69,11 @@ public final class ExportWizardProvider implements WizardProvider { return exportWithLauncher(modpackType, exportInfo, modpackFile); } - private Task exportWithLauncher(String modpackType, ModpackExportInfo exportInfo, File modpackFile) { + private Task exportWithLauncher(String modpackType, ModpackExportInfo exportInfo, Path modpackFile) { Path launcherJar = JarUtils.thisJarPath(); boolean packWithLauncher = exportInfo.isPackWithLauncher() && launcherJar != null; - return new Task() { - File tempModpack; + return new Task<>() { + Path tempModpack; Task exportTask; { @@ -88,9 +87,9 @@ public final class ExportWizardProvider implements WizardProvider { @Override public void preExecute() throws Exception { - File dest; + Path dest; if (packWithLauncher) { - dest = tempModpack = Files.createTempFile("hmcl", ".zip").toFile(); + dest = tempModpack = Files.createTempFile("hmcl", ".zip"); } else { dest = modpackFile; } @@ -122,7 +121,7 @@ public final class ExportWizardProvider implements WizardProvider { @Override public void execute() throws Exception { if (!packWithLauncher) return; - try (Zipper zip = new Zipper(modpackFile.toPath())) { + try (Zipper zip = new Zipper(modpackFile)) { Config exported = new Config(); exported.setBackgroundImageType(config().getBackgroundImageType()); @@ -165,7 +164,7 @@ public final class ExportWizardProvider implements WizardProvider { }; } - private Task exportAsMcbbs(ModpackExportInfo exportInfo, File modpackFile) { + private Task exportAsMcbbs(ModpackExportInfo exportInfo, Path modpackFile) { return new Task() { Task dependency = null; @@ -175,7 +174,7 @@ public final class ExportWizardProvider implements WizardProvider { @Override public void execute() { - dependency = new McbbsModpackExportTask(profile.getRepository(), version, exportInfo, modpackFile.toPath()); + dependency = new McbbsModpackExportTask(profile.getRepository(), version, exportInfo, modpackFile); } @Override @@ -185,7 +184,7 @@ public final class ExportWizardProvider implements WizardProvider { }; } - private Task exportAsMultiMC(ModpackExportInfo exportInfo, File modpackFile) { + private Task exportAsMultiMC(ModpackExportInfo exportInfo, Path modpackFile) { return new Task() { Task dependency; @@ -223,7 +222,7 @@ public final class ExportWizardProvider implements WizardProvider { /* overrideCommands */ true, /* overrideWindow */ true, /* iconKey */ null // TODO - ), modpackFile.toPath()); + ), modpackFile); } @Override @@ -233,7 +232,7 @@ public final class ExportWizardProvider implements WizardProvider { }; } - private Task exportAsServer(ModpackExportInfo exportInfo, File modpackFile) { + private Task exportAsServer(ModpackExportInfo exportInfo, Path modpackFile) { return new Task() { Task dependency; @@ -243,7 +242,7 @@ public final class ExportWizardProvider implements WizardProvider { @Override public void execute() { - dependency = new ServerModpackExportTask(profile.getRepository(), version, exportInfo, modpackFile.toPath()); + dependency = new ServerModpackExportTask(profile.getRepository(), version, exportInfo, modpackFile); } @Override @@ -253,7 +252,7 @@ public final class ExportWizardProvider implements WizardProvider { }; } - private Task exportAsModrinth(ModpackExportInfo exportInfo, File modpackFile) { + private Task exportAsModrinth(ModpackExportInfo exportInfo, Path modpackFile) { return new Task() { Task dependency; @@ -267,7 +266,7 @@ public final class ExportWizardProvider implements WizardProvider { profile.getRepository(), version, exportInfo, - modpackFile.toPath() + modpackFile ); } @@ -280,16 +279,12 @@ public final class ExportWizardProvider implements WizardProvider { @Override public Node createPage(WizardController controller, int step, Map settings) { - switch (step) { - case 0: - return new ModpackTypeSelectionPage(controller); - case 1: - return new ModpackInfoPage(controller, profile.getRepository(), version); - case 2: - return new ModpackFileSelectionPage(controller, profile, version, ModAdviser::suggestMod); - default: - throw new IllegalArgumentException("step"); - } + return switch (step) { + case 0 -> new ModpackTypeSelectionPage(controller); + case 1 -> new ModpackInfoPage(controller, profile.getRepository(), version); + case 2 -> new ModpackFileSelectionPage(controller, profile, version, ModAdviser::suggestMod); + default -> throw new IllegalArgumentException("step"); + }; } @Override 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 a4c44be46..ddfed5bc3 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 @@ -36,7 +36,9 @@ import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -45,6 +47,7 @@ import java.util.Objects; import static org.jackhuang.hmcl.util.Lang.mapOf; import static org.jackhuang.hmcl.util.Pair.pair; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; /** * @author huangyuhui @@ -61,7 +64,7 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard this.adviser = adviser; JFXTreeView treeView = new JFXTreeView<>(); - rootNode = getTreeItem(profile.getRepository().getRunDirectory(version).toFile(), "minecraft"); + rootNode = getTreeItem(profile.getRepository().getRunDirectory(version), "minecraft"); treeView.setRoot(rootNode); treeView.setSelectionModel(new NoneMultipleSelectionModel<>()); this.setCenter(treeView); @@ -80,15 +83,18 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard this.setBottom(nextPane); } - private CheckBoxTreeItem getTreeItem(File file, String basePath) { - if (!file.exists()) + private CheckBoxTreeItem getTreeItem(Path file, String basePath) { + if (Files.notExists(file)) return null; + boolean isDirectory = Files.isDirectory(file); + 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.getName()), version)) state = ModAdviser.ModSuggestion.HIDDEN; - if (file.isDirectory() && Objects.equals(file.getName(), version + "-natives")) // Ignore -natives + state = adviser.advise(StringUtils.substringAfter(basePath, "minecraft/") + (isDirectory ? "/" : ""), isDirectory); + if (!isDirectory && Objects.equals(FileUtils.getNameWithoutExtension(file), version)) + state = ModAdviser.ModSuggestion.HIDDEN; + if (isDirectory && Objects.equals(FileUtils.getName(file), version + "-natives")) // Ignore -natives state = ModAdviser.ModSuggestion.HIDDEN; if (state == ModAdviser.ModSuggestion.HIDDEN) return null; @@ -98,11 +104,10 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard if (state == ModAdviser.ModSuggestion.SUGGESTED) node.setSelected(true); - if (file.isDirectory()) { - File[] files = file.listFiles(); - if (files != null) { - for (File it : files) { - CheckBoxTreeItem subNode = getTreeItem(it, basePath + "/" + it.getName()); + if (isDirectory) { + try (var stream = Files.list(file)) { + stream.forEach(it -> { + CheckBoxTreeItem subNode = getTreeItem(it, basePath + "/" + FileUtils.getName(it)); if (subNode != null) { node.setSelected(subNode.isSelected() || node.isSelected()); if (!subNode.isSelected()) { @@ -110,8 +115,11 @@ public final class ModpackFileSelectionPage extends BorderPane implements Wizard } node.getChildren().add(subNode); } - } + }); + } catch (IOException e) { + LOG.warning("Failed to list contents of " + file, e); } + if (!node.isSelected()) node.setIndeterminate(false); // Empty folder need not to be displayed. diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java index 1e7d4d22f..c9e55fbd8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java @@ -42,10 +42,11 @@ import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.wizard.WizardController; import org.jackhuang.hmcl.ui.wizard.WizardPage; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.platform.SystemInfo; -import java.io.File; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -112,7 +113,7 @@ public final class ModpackInfoPage extends Control implements WizardPage { fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); fileChooser.setInitialFileName(name.get() + ".zip"); } - File file = fileChooser.showSaveDialog(Controllers.getStage()); + Path file = FileUtils.toPath(fileChooser.showSaveDialog(Controllers.getStage())); if (file == null) { controller.onEnd(); return; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaDownloadDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaDownloadDialog.java index adb3d611b..94faea763 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaDownloadDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaDownloadDialog.java @@ -52,8 +52,9 @@ import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.Platform; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.CancellationException; import java.util.function.Consumer; @@ -340,8 +341,8 @@ public final class JavaDownloadDialog extends StackPane { if (StringUtils.isBlank(fileInfo.getDirectDownloadUri())) throw new IOException("Missing download URI: " + json); - File targetFile = File.createTempFile("hmcl-java-", "." + version.getArchiveType()); - targetFile.deleteOnExit(); + Path targetFile = Files.createTempFile("hmcl-java-", "." + version.getArchiveType()); + targetFile.toFile().deleteOnExit(); Task getIntegrityCheck; if (StringUtils.isNotBlank(fileInfo.getChecksum())) @@ -363,8 +364,8 @@ public final class JavaDownloadDialog extends StackPane { return getIntegrityCheck .thenComposeAsync(integrityCheck -> new FileDownloadTask(downloadProvider.injectURLWithCandidates(fileInfo.getDirectDownloadUri()), - targetFile.toPath(), integrityCheck).setName(fileInfo.getFileName())) - .thenSupplyAsync(targetFile::toPath); + targetFile, integrityCheck).setName(fileInfo.getFileName())) + .thenSupplyAsync(() -> targetFile); }) .whenComplete(Schedulers.javafx(), ((result, exception) -> { if (exception == null) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java index d05433f9f..f56b35ed7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java @@ -45,6 +45,7 @@ import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.TaskCancellationAction; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.UnsupportedPlatformException; import org.jackhuang.hmcl.util.tree.ArchiveFileTree; import org.jackhuang.hmcl.util.platform.Architecture; @@ -79,19 +80,19 @@ public final class JavaManagementPage extends ListPageBase { - String name = it.getName(); - return it.isDirectory() || name.endsWith(".zip") || name.endsWith(".tar.gz") || name.equals(OperatingSystem.CURRENT_OS.getJavaExecutable()); + String name = FileUtils.getName(it); + return Files.isDirectory(it) || name.endsWith(".zip") || name.endsWith(".tar.gz") || name.equals(OperatingSystem.CURRENT_OS.getJavaExecutable()); }, files -> { - for (File file : files) { - if (file.isDirectory()) { - onAddJavaHome(file.toPath()); + for (Path file : files) { + if (Files.isDirectory(file)) { + onAddJavaHome(file); } else { - String fileName = file.getName(); + String fileName = FileUtils.getName(file); if (fileName.equals(OperatingSystem.CURRENT_OS.getJavaExecutable())) { - onAddJavaBinary(file.toPath()); + onAddJavaBinary(file); } else if (fileName.endsWith(".zip") || fileName.endsWith(".tar.gz")) { - onInstallArchive(file.toPath()); + onInstallArchive(file); } else { throw new AssertionError("Unreachable code"); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java index 257d492ad..4206d3329 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/RootPage.java @@ -51,7 +51,8 @@ import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.versioning.VersionNumber; -import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Instant; import java.util.Comparator; import java.util.List; @@ -92,16 +93,16 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { if (mainPage == null) { MainPage mainPage = new MainPage(); FXUtils.applyDragListener(mainPage, - file -> ModpackHelper.isFileModpackByExtension(file) || NBTFileType.isNBTFileByExtension(file.toPath()), + file -> ModpackHelper.isFileModpackByExtension(file) || NBTFileType.isNBTFileByExtension(file), modpacks -> { - File file = modpacks.get(0); + Path file = modpacks.get(0); if (ModpackHelper.isFileModpackByExtension(file)) { Controllers.getDecorator().startWizard( new ModpackInstallWizardProvider(Profiles.getSelectedProfile(), file), i18n("install.modpack")); - } else if (NBTFileType.isNBTFileByExtension(file.toPath())) { + } else if (NBTFileType.isNBTFileByExtension(file)) { try { - Controllers.navigate(new NBTEditorPage(file.toPath())); + Controllers.navigate(new NBTEditorPage(file)); } catch (Throwable e) { LOG.warning("Fail to open nbt file", e); Controllers.dialog(i18n("nbt.open.failed") + "\n\n" + StringUtils.getStackTrace(e), @@ -211,14 +212,13 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage { checkedModpack = true; if (repository.getVersionCount() == 0) { - File modpackFile = new File("modpack.zip").getAbsoluteFile(); - if (modpackFile.exists()) { - Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath())) + Path modpackFile = Metadata.CURRENT_DIRECTORY.resolve("modpack.zip"); + if (Files.exists(modpackFile)) { + Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(modpackFile)) .thenApplyAsync( - encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding)) + encoding -> ModpackHelper.readModpackManifest(modpackFile, encoding)) .thenApplyAsync(modpack -> ModpackHelper - .getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), - modpack) + .getInstallTask(repository.getProfile(), modpackFile.toFile(), modpack.getName(), modpack) .executor()) .thenAcceptAsync(Schedulers.javafx(), executor -> { Controllers.taskDialog(executor, i18n("modpack.installing"), TaskCancellationAction.NO_CANCEL); 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 04f319fdd..5c834a736 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 @@ -29,7 +29,6 @@ import org.jackhuang.hmcl.ui.ListPageBase; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.javafx.MappedObservableList; -import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.List; @@ -50,13 +49,13 @@ public final class DatapackListPage extends ListPageBase Objects.equals("zip", FileUtils.getExtension(it.getName())), + FXUtils.applyDragListener(this, it -> Objects.equals("zip", FileUtils.getExtension(it)), mods -> mods.forEach(this::installSingleDatapack), this::refresh); } - private void installSingleDatapack(File datapack) { + private void installSingleDatapack(Path datapack) { try { - Datapack zip = new Datapack(datapack.toPath()); + Datapack zip = new Datapack(datapack); zip.loadFromZip(); zip.installTo(worldDir); } catch (IOException | IllegalArgumentException e) { @@ -82,7 +81,7 @@ public final class DatapackListPage extends ListPageBase res = chooser.showOpenMultipleDialog(Controllers.getStage()); + List res = FileUtils.toPaths(chooser.showOpenMultipleDialog(Controllers.getStage())); if (res != null) res.forEach(this::installSingleDatapack); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java index 4ef27ef16..614b991ab 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java @@ -49,11 +49,12 @@ import org.jackhuang.hmcl.ui.construct.*; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.i18n.I18n; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.javafx.BindingMapping; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jetbrains.annotations.Nullable; -import java.io.File; +import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -172,14 +173,14 @@ public class DownloadPage extends Control implements DecoratorPage { fileChooser.setTitle(i18n("button.save_as")); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("file"), "*." + extension)); fileChooser.setInitialFileName(file.getFile().getFilename()); - File dest = fileChooser.showSaveDialog(Controllers.getStage()); + Path dest = FileUtils.toPath(fileChooser.showSaveDialog(Controllers.getStage())); if (dest == null) { return; } Controllers.taskDialog( Task.composeAsync(() -> { - var task = new FileDownloadTask(file.getFile().getUrl(), dest.toPath(), file.getFile().getIntegrityCheck()); + var task = new FileDownloadTask(file.getFile().getUrl(), dest, file.getFile().getIntegrityCheck()); task.setName(file.getName()); return task; }), 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 2a6e4c040..2f487ff1d 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 @@ -33,7 +33,7 @@ import org.jackhuang.hmcl.ui.download.UpdateInstallerWizardProvider; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -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.getName())), mods -> { + FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "exe").contains(FileUtils.getExtension(it)), mods -> { if (!mods.isEmpty()) doInstallOffline(mods.get(0)); }); @@ -137,12 +137,12 @@ public class InstallerListPage extends ListPageBase implements Ve public void installOffline() { FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("install.installer.install_offline.extension"), "*.jar", "*.exe")); - File file = chooser.showOpenDialog(Controllers.getStage()); + Path file = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (file != null) doInstallOffline(file); } - private void doInstallOffline(File file) { - Task task = profile.getDependency().installLibraryAsync(version, file.toPath()) + private void doInstallOffline(Path file) { + Task task = profile.getDependency().installLibraryAsync(version, file) .thenComposeAsync(profile.getRepository()::saveAsync) .thenComposeAsync(profile.getRepository().refreshVersionsAsync()); task.setName(i18n("install.installer.install_offline")); 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 ef41185ee..79e771948 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 @@ -39,9 +39,9 @@ import org.jackhuang.hmcl.ui.construct.PageAware; import org.jackhuang.hmcl.util.TaskCancellationAction; import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -59,10 +59,10 @@ public final class ModListPage extends ListPageBase Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it.getName())), mods -> { + FXUtils.applyDragListener(this, it -> Arrays.asList("jar", "zip", "litemod").contains(FileUtils.getExtension(it)), mods -> { mods.forEach(it -> { try { - modManager.addMod(it.toPath()); + modManager.addMod(it); } catch (IOException | IllegalArgumentException e) { LOG.warning("Unable to parse mod file " + it, e); } @@ -117,7 +117,7 @@ public final class ModListPage extends ListPageBase res = chooser.showOpenMultipleDialog(Controllers.getStage()); + List res = FileUtils.toPaths(chooser.showOpenMultipleDialog(Controllers.getStage())); if (res == null) return; @@ -126,13 +126,13 @@ public final class ModListPage extends ListPageBase failed = new ArrayList<>(); Task.runAsync(() -> { - for (File file : res) { + for (Path file : res) { try { - modManager.addMod(file.toPath()); - succeeded.add(file.getName()); + modManager.addMod(file); + succeeded.add(FileUtils.getName(file)); } catch (Exception e) { LOG.warning("Unable to add mod " + file, e); - failed.add(file.getName()); + failed.add(FileUtils.getName(file)); // Actually addMod will not throw exceptions because FileChooser has already filtered files. } 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 3e9889b58..64980d3f9 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 @@ -45,14 +45,12 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; @@ -76,8 +74,8 @@ public final class SchematicsPage extends ListPageBase impl public SchematicsPage() { FXUtils.applyDragListener(this, - file -> currentDirectory != null && file.isFile() && file.getName().endsWith(".litematic"), - files -> addFiles(files.stream().map(File::toPath).collect(Collectors.toList())) + file -> currentDirectory != null && Files.isRegularFile(file) && FileUtils.getName(file).endsWith(".litematic"), + this::addFiles ); } @@ -149,9 +147,9 @@ public final class SchematicsPage extends ListPageBase impl fileChooser.setTitle(i18n("schematics.add")); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter( i18n("schematics"), "*.litematic")); - List files = fileChooser.showOpenMultipleDialog(Controllers.getStage()); + List files = FileUtils.toPaths(fileChooser.showOpenMultipleDialog(Controllers.getStage())); if (files != null && !files.isEmpty()) { - addFiles(files.stream().map(File::toPath).collect(Collectors.toList())); + addFiles(files); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java index a008eac50..aefb20b83 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionIconDialog.java @@ -31,9 +31,10 @@ import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; import org.jackhuang.hmcl.ui.construct.DialogPane; import org.jackhuang.hmcl.ui.construct.RipplerContainer; +import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; import java.io.IOException; +import java.nio.file.Path; import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -75,10 +76,10 @@ public class VersionIconDialog extends DialogPane { private void exploreIcon() { FileChooser chooser = new FileChooser(); chooser.getExtensionFilters().add(FXUtils.getImageExtensionFilter()); - File selectedFile = chooser.showOpenDialog(Controllers.getStage()); + Path selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); if (selectedFile != null) { try { - profile.getRepository().setVersionIconFile(versionId, selectedFile.toPath()); + profile.getRepository().setVersionIconFile(versionId, selectedFile); if (vs != null) { vs.setVersionIcon(VersionIconType.DEFAULT); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index ae5d5ce0f..6850e6fb7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -44,7 +44,6 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; -import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.Files; @@ -89,9 +88,9 @@ public final class Versions { .whenComplete(Schedulers.javafx(), e -> { if (e == null) { if (version != null) { - Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(profile, modpack.toFile(), version)); + Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(profile, modpack, version)); } else { - Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(profile, modpack.toFile())); + Controllers.getDecorator().startWizard(new ModpackInstallWizardProvider(profile, modpack)); } } else if (e instanceof CancellationException) { Controllers.showToast(i18n("message.cancelled")); @@ -200,9 +199,9 @@ public final class Versions { ? new FileChooser.ExtensionFilter(i18n("extension.bat"), "*.bat") : new FileChooser.ExtensionFilter(i18n("extension.sh"), "*.sh")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("extension.ps1"), "*.ps1")); - File file = chooser.showSaveDialog(Controllers.getStage()); + Path file = FileUtils.toPath(chooser.showSaveDialog(Controllers.getStage())); if (file != null) - new LauncherHelper(profile, account, id).makeLaunchScript(file); + new LauncherHelper(profile, account, id).makeLaunchScript(file.toFile()); }); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java index 2acba82d7..acfa9de8d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/WorldListItem.java @@ -29,8 +29,8 @@ import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; import java.nio.file.Path; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -60,12 +60,12 @@ public final class WorldListItem extends Control { fileChooser.setTitle(i18n("world.export.title")); fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("world"), "*.zip")); fileChooser.setInitialFileName(world.getWorldName()); - File file = fileChooser.showSaveDialog(Controllers.getStage()); + Path file = FileUtils.toPath(fileChooser.showSaveDialog(Controllers.getStage())); if (file == null) { return; } - Controllers.getDecorator().startWizard(new SinglePageWizardProvider(controller -> new WorldExportPage(world, file.toPath(), controller::onFinish))); + Controllers.getDecorator().startWizard(new SinglePageWizardProvider(controller -> new WorldExportPage(world, file, controller::onFinish))); } public void delete() { 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 f5913b35c..e39162f31 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 @@ -29,7 +29,6 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.*; import org.jackhuang.hmcl.util.io.FileUtils; -import java.io.File; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.InvalidPathException; @@ -53,7 +52,7 @@ public final class WorldListPage extends ListPageBase implements private String gameVersion; public WorldListPage() { - FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it.getName())), modpacks -> { + FXUtils.applyDragListener(this, it -> "zip".equals(FileUtils.getExtension(it)), modpacks -> { installWorld(modpacks.get(0)); }); @@ -112,7 +111,7 @@ public final class WorldListPage extends ListPageBase implements FileChooser chooser = new FileChooser(); chooser.setTitle(i18n("world.import.choose")); chooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter(i18n("world.extension"), "*.zip")); - List res = chooser.showOpenMultipleDialog(Controllers.getStage()); + List res = FileUtils.toPaths(chooser.showOpenMultipleDialog(Controllers.getStage())); if (res == null || res.isEmpty()) return; installWorld(res.get(0)); @@ -123,10 +122,10 @@ public final class WorldListPage extends ListPageBase implements Controllers.navigate(Controllers.getDownloadPage()); } - private void installWorld(File zipFile) { + private void installWorld(Path zipFile) { // Only accept one world file because user is required to confirm the new world name // Or too many input dialogs are popped. - Task.supplyAsync(() -> new World(zipFile.toPath())) + Task.supplyAsync(() -> new World(zipFile)) .whenComplete(Schedulers.javafx(), world -> { Controllers.prompt(i18n("world.name.enter"), (name, resolve, reject) -> { Task.runAsync(() -> world.install(savesDir, name)) 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 74a2c90c1..9aec3935f 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 @@ -26,10 +26,7 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.BufferedWriter; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStream; +import java.io.*; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.time.ZonedDateTime; @@ -50,6 +47,28 @@ public final class FileUtils { private FileUtils() { } + public static @Nullable Path toPath(@Nullable File file) { + try { + return file != null ? file.toPath() : null; + } catch (InvalidPathException e) { + LOG.warning("Invalid path: " + file); + return null; + } + } + + public static @Nullable List toPaths(@Nullable List files) { + if (files == null) return null; + return files.stream().map(FileUtils::toPath).filter(Objects::nonNull).toList(); + } + + public static @Nullable File toFile(@Nullable Path file) { + try { + return file != null ? file.toFile() : null; + } catch (UnsupportedOperationException ignored) { + return null; + } + } + public static boolean canCreateDirectory(String path) { try { return canCreateDirectory(Paths.get(path));