diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index d684dbbbb..c980ee78a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -122,14 +122,14 @@ public final class LauncherHelper { Optional gameVersion = GameVersion.minecraftVersion(repository.getVersionJar(version)); TaskExecutor executor = Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.DEPENDENCIES)) - .then(variables -> { + .then(() -> { if (setting.isNotCheckGame()) return null; else return dependencyManager.checkGameCompletionAsync(version); }) .then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.MODS))) - .then(var -> { + .then(() -> { try { ModpackConfiguration configuration = ModpackHelper.readModpackConfiguration(repository.getModpackConfiguration(selectedVersion)); if ("Curse".equals(configuration.getType())) @@ -141,44 +141,42 @@ public final class LauncherHelper { } }) .then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LOGGING_IN))) - .then(Task.of(i18n("account.methods"), variables -> { + .thenTaskResult(() -> Task.ofResult(i18n("account.methods"), () -> { try { - variables.set("account", account.logIn()); + return account.logIn(); } catch (CredentialExpiredException e) { LOG.info("Credential has expired: " + e); - variables.set("account", DialogController.logIn(account)); + return DialogController.logIn(account); } catch (AuthenticationException e) { LOG.warning("Authentication failed, try playing offline: " + e); - variables.set("account", - account.playOffline().orElseThrow(() -> e)); + return account.playOffline().orElseThrow(() -> e); } })) - .then(Task.of(Schedulers.javafx(), () -> emitStatus(LoadingState.LAUNCHING))) - .then(Task.of(variables -> { - variables.set("launcher", new HMCLGameLauncher( - repository, - selectedVersion, - variables.get("account"), - setting.toLaunchOptions(profile.getGameDir()), - launcherVisibility == LauncherVisibility.CLOSE - ? null // Unnecessary to start listening to game process output when close launcher immediately after game launched. - : new HMCLProcessListener(variables.get("account"), setting, gameVersion.isPresent()) - )); - })) - .then(variables -> { - DefaultLauncher launcher = variables.get("launcher"); + .thenResult(Schedulers.javafx(), authInfo -> { + emitStatus(LoadingState.LAUNCHING); + return authInfo; + }) + .thenResult(authInfo -> new HMCLGameLauncher( + repository, + selectedVersion, + authInfo, + setting.toLaunchOptions(profile.getGameDir()), + launcherVisibility == LauncherVisibility.CLOSE + ? null // Unnecessary to start listening to game process output when close launcher immediately after game launched. + : new HMCLProcessListener(authInfo, setting, gameVersion.isPresent()) + )) + .thenTaskResult(launcher -> { // launcher is prev task's result if (scriptFile == null) { return new LaunchTask<>(launcher::launch).setName(i18n("version.launch")); } else { - return new LaunchTask<>(() -> { + return new LaunchTask(() -> { launcher.makeLaunchScript(scriptFile); return null; }).setName(i18n("version.launch_script")); } }) - .then(Task.of(variables -> { + .thenVoid(process -> { // process is LaunchTask's result if (scriptFile == null) { - ManagedProcess process = variables.get(LaunchTask.LAUNCH_ID); PROCESSES.add(process); if (launcherVisibility == LauncherVisibility.CLOSE) Launcher.stopApplication(); @@ -187,13 +185,13 @@ public final class LauncherHelper { process.stop(); it.fireEvent(new DialogCloseEvent()); }); - } else + } else { Platform.runLater(() -> { launchingStepsPane.fireEvent(new DialogCloseEvent()); Controllers.dialog(i18n("version.launch_script.success", scriptFile.getAbsolutePath())); }); - - })) + } + }) .executor(); launchingStepsPane.setExecutor(executor, false); @@ -429,13 +427,6 @@ public final class LauncherHelper { public void execute() throws Exception { setResult(supplier.get()); } - - @Override - public String getId() { - return LAUNCH_ID; - } - - static final String LAUNCH_ID = "launch"; } /** 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 eb7be914d..c4bfdac61 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -110,13 +110,13 @@ public final class ModpackHelper { if (modpack.getManifest() instanceof CurseManifest) return new CurseInstallTask(profile.getDependency(), zipFile, modpack, ((CurseManifest) modpack.getManifest()), name) - .finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure); + .finalized(Schedulers.defaultScheduler(), success, failure); else if (modpack.getManifest() instanceof HMCLModpackManifest) return new HMCLModpackInstallTask(profile, zipFile, modpack, name) - .finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure); + .finalized(Schedulers.defaultScheduler(), success, failure); else if (modpack.getManifest() instanceof MultiMCInstanceConfiguration) return new MultiMCModpackInstallTask(profile.getDependency(), zipFile, modpack, ((MultiMCInstanceConfiguration) modpack.getManifest()), name) - .finalized(Schedulers.defaultScheduler(), ExceptionalConsumer.fromRunnable(success), failure) + .finalized(Schedulers.defaultScheduler(), success, failure) .then(new MultiMCInstallVersionSettingTask(profile, ((MultiMCInstanceConfiguration) modpack.getManifest()), name)); else throw new IllegalStateException("Unrecognized modpack: " + modpack); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java index bda00a079..0479f27d0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LeftPaneController.java @@ -121,13 +121,12 @@ public final class LeftPaneController extends AdvancedListBox { if (modpackFile.exists()) { Task.ofResult(() -> CompressingUtils.findSuitableEncoding(modpackFile.toPath())) .thenResult(encoding -> ModpackHelper.readModpackManifest(modpackFile.toPath(), encoding)) - .thenResult(modpack -> { + .thenVoid(modpack -> { AtomicReference region = new AtomicReference<>(); TaskExecutor executor = ModpackHelper.getInstallTask(repository.getProfile(), modpackFile, modpack.getName(), modpack) .with(Task.of(Schedulers.javafx(), this::checkAccount)).executor(); region.set(Controllers.taskDialog(executor, i18n("modpack.installing"))); executor.start(); - return null; }).start(); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java index 4e739e05e..01b6ae552 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AccountLoginPane.java @@ -62,7 +62,7 @@ public class AccountLoginPane extends StackPane { progressBar.setVisible(true); lblCreationWarning.setText(""); Task.ofResult(() -> oldAccount.logInWithPassword(password)) - .finalizedResult(Schedulers.javafx(), authInfo -> { + .finalized(Schedulers.javafx(), authInfo -> { success.accept(authInfo); fireEvent(new DialogCloseEvent()); progressBar.setVisible(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java index 06744aa4c..32c76b49f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAccountPane.java @@ -196,7 +196,7 @@ public class AddAccountPane extends StackPane { Object additionalData = getAuthAdditionalData(); Task.ofResult(() -> factory.create(new Selector(), username, password, additionalData)) - .finalizedResult(Schedulers.javafx(), account -> { + .finalized(Schedulers.javafx(), account -> { int oldIndex = Accounts.getAccounts().indexOf(account); if (oldIndex == -1) { Accounts.getAccounts().add(account); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java index 68f2be8d8..85ec12ba1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/account/AddAuthlibInjectorServerPane.java @@ -106,7 +106,7 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa Task.of(() -> { serverBeingAdded = AuthlibInjectorServer.locateServer(url); - }).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded, exception) -> { + }).finalized(Schedulers.javafx(), (isDependentsSucceeded, exception) -> { addServerPane.setDisable(false); nextPane.hideSpinner(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java index b245aac4c..fe5f651c5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/InstallerWizardProvider.java @@ -27,6 +27,7 @@ import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.task.DownloadException; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType; import org.jackhuang.hmcl.ui.wizard.WizardController; @@ -89,16 +90,16 @@ public final class InstallerWizardProvider implements WizardProvider { settings.put("success_message", i18n("install.success")); settings.put("failure_callback", (FailureCallback) (settings1, exception, next) -> alertFailureMessage(exception, next)); - Task ret = Task.ofResult("version", () -> version); + TaskResult ret = Task.ofResult(() -> version); if (settings.containsKey("forge")) - ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge"))); + ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("forge"))); if (settings.containsKey("liteloader")) - ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader"))); + ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("liteloader"))); if (settings.containsKey("optifine")) - ret = ret.then(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine"))); + ret = ret.thenTaskResult(profile.getDependency().installLibraryAsync((RemoteVersion) settings.get("optifine"))); return ret.then(profile.getRepository().refreshVersionsAsync()); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java index 82ee5b12b..0149028f2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackPage.java @@ -111,7 +111,7 @@ public final class ModpackPage extends StackPane implements WizardPage { spinnerPane.showSpinner(); Task.ofResult(() -> CompressingUtils.findSuitableEncoding(selectedFile.toPath())) .thenResult(encoding -> manifest = ModpackHelper.readModpackManifest(selectedFile.toPath(), encoding)) - .finalizedResult(Schedulers.javafx(), manifest -> { + .finalized(Schedulers.javafx(), manifest -> { spinnerPane.hideSpinner(); controller.getSettings().put(MODPACK_MANIFEST, manifest); lblName.setText(manifest.getName()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java index 087e8cae3..9a3e6bcbf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VanillaInstallWizardProvider.java @@ -59,7 +59,7 @@ public final class VanillaInstallWizardProvider implements WizardProvider { if (settings.containsKey("optifine")) builder.version((RemoteVersion) settings.get("optifine")); - return builder.buildAsync().finalized((a, b, c) -> profile.getRepository().refreshVersions()) + return builder.buildAsync().finalized((a, b) -> profile.getRepository().refreshVersions()) .then(Task.of(Schedulers.javafx(), () -> profile.setSelectedVersion(name))); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index e7d64e8b7..1787606c3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -128,7 +128,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres @Override public void refresh() { transitionHandler.setContent(spinner, ContainerAnimations.FADE.getAnimationProducer()); - executor = versionList.refreshAsync(gameVersion, downloadProvider).finalized((variables, isDependentsSucceeded, exception) -> { + executor = versionList.refreshAsync(gameVersion, downloadProvider).finalized((isDependentsSucceeded, exception) -> { if (isDependentsSucceeded) { List items = loadVersions(); 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 2b45437a4..020ff62de 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 @@ -54,7 +54,7 @@ public class InstallerListPage extends ListPage { LinkedList newList = new LinkedList<>(version.getLibraries()); newList.remove(library); new MaintainTask(version.setLibraries(newList)) - .then(variables -> new VersionJsonSaveTask(profile.getRepository(), variables.get(MaintainTask.ID))) + .then(maintainedVersion -> new VersionJsonSaveTask(profile.getRepository(), maintainedVersion)) .with(profile.getRepository().refreshVersionsAsync()) .with(Task.of(Schedulers.javafx(), () -> loadVersion(this.profile, this.versionId))) .start(); 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 904b9801a..c420c5d5d 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 @@ -36,7 +36,6 @@ import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; -import org.jackhuang.hmcl.ui.ListPage; import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.io.FileUtils; @@ -86,13 +85,13 @@ public final class ModListPage extends Control { public void loadMods(ModManager modManager) { this.modManager = modManager; - Task.ofResult("list", variables -> { + Task.ofResult(() -> { synchronized (ModListPage.this) { JFXUtilities.runInFX(() -> loadingProperty().set(true)); modManager.refreshMods(); return new LinkedList<>(modManager.getMods()); } - }).finalizedResult(Schedulers.javafx(), (list, isDependentsSucceeded, exception) -> { + }).finalized(Schedulers.javafx(), (list, isDependentsSucceeded, exception) -> { loadingProperty().set(false); if (isDependentsSucceeded) FXUtils.onWeakChangeAndOperate(parentTab.getSelectionModel().selectedItemProperty(), newValue -> { @@ -112,7 +111,7 @@ public final class ModListPage extends Control { List succeeded = new LinkedList<>(); List failed = new LinkedList<>(); if (res == null) return; - Task.of(variables -> { + Task.of(() -> { for (File file : res) { try { modManager.addMod(file); @@ -124,7 +123,7 @@ public final class ModListPage extends Control { // Actually addMod will not throw exceptions because FileChooser has already filtered files. } } - }).with(Task.of(Schedulers.javafx(), variables -> { + }).with(Task.of(Schedulers.javafx(), () -> { List prompt = new LinkedList<>(); if (!succeeded.isEmpty()) prompt.add(i18n("mods.add.success", String.join(", ", succeeded))); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java index dc2fbad09..be654a339 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionSettingsPage.java @@ -57,6 +57,7 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Optional; import java.util.logging.Level; import java.util.stream.Collectors; @@ -117,14 +118,13 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag FXUtils.smoothScrolling(scroll); - Task.ofResult(JavaVersion::getJavas).thenResult(Schedulers.javafx(), list -> { + Task.ofResult(JavaVersion::getJavas).thenVoid(Schedulers.javafx(), list -> { javaItem.loadChildren(list.stream() .map(javaVersion -> javaItem.createChildren(javaVersion.getVersion() + i18n("settings.game.java_directory.bit", javaVersion.getPlatform().getBit()), javaVersion.getBinary().toString(), javaVersion)) .collect(Collectors.toList())); javaItemsLoaded = true; initializeSelectedJava(); - return null; }).start(); javaItem.setSelectedData(null); @@ -273,10 +273,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag VersionSetting versionSetting = lastVersionSetting; if (versionSetting == null) return; - Task.of(variables -> variables.set("java", versionSetting.getJavaVersion())) - .subscribe(Task.of(Schedulers.javafx(), - variables -> javaItem.setSubtitle(variables.getOptional("java") - .map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path")))); + Task.ofResult(versionSetting::getJavaVersion) + .thenVoid(Schedulers.javafx(), javaVersion -> javaItem.setSubtitle(Optional.ofNullable(javaVersion) + .map(JavaVersion::getBinary).map(Path::toString).orElse("Invalid Java Path"))) + .start(); } @FXML 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 b7592469e..195da3bd7 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.util.Logging; import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; -import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Path; import java.util.List; @@ -52,7 +51,7 @@ public class WorldListPage extends ListPage { setLoading(true); Task.ofResult(() -> World.getWorlds(savesDir).parallel().collect(Collectors.toList())) - .finalizedResult(Schedulers.javafx(), (result, isDependentsSucceeded, exception) -> { + .finalized(Schedulers.javafx(), (result, isDependentsSucceeded, exception) -> { setLoading(false); if (isDependentsSucceeded) itemsProperty().setAll(result.stream().map(WorldListItem::new).collect(Collectors.toList())); @@ -74,10 +73,10 @@ public class WorldListPage extends ListPage { // Only accept one world file because user is required to confirm the new world name // Or too many input dialogs are popped. Task.ofResult(() -> new World(zipFile.toPath())) - .finalizedResult(Schedulers.javafx(), world -> { + .finalized(Schedulers.javafx(), world -> { Controllers.inputDialog(i18n("world.name.enter"), (name, resolve, reject) -> { Task.of(() -> world.install(savesDir, name)) - .finalized(Schedulers.javafx(), var -> { + .finalized(Schedulers.javafx(), () -> { itemsProperty().add(new WorldListItem(new World(savesDir.resolve(name)))); resolve.run(); }, e -> { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java index d121b4b33..c094c719c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultDependencyManager.java @@ -28,7 +28,7 @@ import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.ParallelTask; import org.jackhuang.hmcl.task.Task; -import org.jackhuang.hmcl.util.AutoTypingMap; +import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.util.function.ExceptionalFunction; /** @@ -71,7 +71,7 @@ public class DefaultDependencyManager extends AbstractDependencyManager { @Override public Task checkGameCompletionAsync(Version version) { return new ParallelTask( - Task.ofThen(var -> { + Task.ofThen(() -> { if (!repository.getVersionJar(version).exists()) return new GameDownloadTask(this, null, version); else @@ -88,36 +88,32 @@ public class DefaultDependencyManager extends AbstractDependencyManager { } @Override - public Task installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) { + public TaskResult installLibraryAsync(String gameVersion, Version version, String libraryId, String libraryVersion) { VersionList versionList = getVersionList(libraryId); return versionList.loadAsync(gameVersion, getDownloadProvider()) - .then(variables -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion) + .thenTaskResult(() -> installLibraryAsync(version, versionList.getVersion(gameVersion, libraryVersion) .orElseThrow(() -> new IllegalStateException("Remote library " + libraryId + " has no version " + libraryVersion)))); } @Override - public Task installLibraryAsync(Version version, RemoteVersion libraryVersion) { + public TaskResult installLibraryAsync(Version oldVersion, RemoteVersion libraryVersion) { + TaskResult task; if (libraryVersion instanceof ForgeRemoteVersion) - return new ForgeInstallTask(this, version, (ForgeRemoteVersion) libraryVersion) - .then(variables -> new LibrariesUniqueTask(variables.get("version"))) - .then(variables -> new MaintainTask(variables.get("version"))) - .then(variables -> new VersionJsonSaveTask(repository, variables.get("version"))); + task = new ForgeInstallTask(this, oldVersion, (ForgeRemoteVersion) libraryVersion); else if (libraryVersion instanceof LiteLoaderRemoteVersion) - return new LiteLoaderInstallTask(this, version, (LiteLoaderRemoteVersion) libraryVersion) - .then(variables -> new LibrariesUniqueTask(variables.get("version"))) - .then(variables -> new MaintainTask(variables.get("version"))) - .then(variables -> new VersionJsonSaveTask(repository, variables.get("version"))); + task = new LiteLoaderInstallTask(this, oldVersion, (LiteLoaderRemoteVersion) libraryVersion); else if (libraryVersion instanceof OptiFineRemoteVersion) - return new OptiFineInstallTask(this, version, (OptiFineRemoteVersion) libraryVersion) - .then(variables -> new LibrariesUniqueTask(variables.get("version"))) - .then(variables -> new MaintainTask(variables.get("version"))) - .then(variables -> new VersionJsonSaveTask(repository, variables.get("version"))); + task = new OptiFineInstallTask(this, oldVersion, (OptiFineRemoteVersion) libraryVersion); else throw new IllegalArgumentException("Remote library " + libraryVersion + " is unrecognized."); + return task + .thenTaskResult(LibrariesUniqueTask::new) + .thenTaskResult(MaintainTask::new) + .thenTaskResult(newVersion -> new VersionJsonSaveTask(repository, newVersion)); } - public ExceptionalFunction, Task, ?> installLibraryAsync(RemoteVersion libraryVersion) { - return var -> installLibraryAsync(var.get("version"), libraryVersion); + public ExceptionalFunction, ?> installLibraryAsync(RemoteVersion libraryVersion) { + return version -> installLibraryAsync(version, libraryVersion); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java index 19045d5ac..885517422 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/DefaultGameBuilder.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.game.*; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.ParallelTask; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.util.AutoTypingMap; import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -49,34 +50,35 @@ public class DefaultGameBuilder extends GameBuilder { @Override public Task buildAsync() { - return new VersionJsonDownloadTask(gameVersion, dependencyManager).then(variables -> { - Version version = JsonUtils.GSON.fromJson(variables.get(VersionJsonDownloadTask.ID), Version.class); - version = version.setId(name).setJar(null); - variables.set("version", version); - Task result = downloadGameAsync(gameVersion, version).then(new ParallelTask( + return new VersionJsonDownloadTask(gameVersion, dependencyManager).thenTaskResult(rawJson -> { + Version original = JsonUtils.GSON.fromJson(rawJson, Version.class); + Version version = original.setId(name).setJar(null); + Task vanillaTask = downloadGameAsync(gameVersion, version).then(new ParallelTask( new GameAssetDownloadTask(dependencyManager, version, GameAssetDownloadTask.DOWNLOAD_INDEX_FORCIBLY), new GameLibrariesTask(dependencyManager, version) // Game libraries will be downloaded for multiple times partly, this time is for vanilla libraries. ).with(new VersionJsonSaveTask(dependencyManager.getGameRepository(), version))); // using [with] because download failure here are tolerant. + TaskResult libraryTask = vanillaTask.thenResult(() -> version); + if (toolVersions.containsKey("forge")) - result = result.then(libraryTaskHelper(gameVersion, "forge")); + libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "forge")); if (toolVersions.containsKey("liteloader")) - result = result.then(libraryTaskHelper(gameVersion, "liteloader")); + libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "liteloader")); if (toolVersions.containsKey("optifine")) - result = result.then(libraryTaskHelper(gameVersion, "optifine")); + libraryTask = libraryTask.thenTaskResult(libraryTaskHelper(gameVersion, "optifine")); for (RemoteVersion remoteVersion : remoteVersions) - result = result.then(dependencyManager.installLibraryAsync(remoteVersion)); + libraryTask = libraryTask.thenTaskResult(dependencyManager.installLibraryAsync(remoteVersion)); - return result; - }).finalized((variables, isDependentsSucceeded, exception) -> { + return libraryTask; + }).finalized((isDependentsSucceeded, exception) -> { if (!isDependentsSucceeded) dependencyManager.getGameRepository().getVersionRoot(name).delete(); }); } - private ExceptionalFunction, Task, ?> libraryTaskHelper(String gameVersion, String libraryId) { - return variables -> dependencyManager.installLibraryAsync(gameVersion, variables.get("version"), libraryId, toolVersions.get(libraryId)); + private ExceptionalFunction, ?> libraryTaskHelper(String gameVersion, String libraryId) { + return version -> dependencyManager.installLibraryAsync(gameVersion, version, libraryId, toolVersions.get(libraryId)); } protected Task downloadGameAsync(String gameVersion, Version version) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java index 919998665..c0a58dcbc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/MaintainTask.java @@ -23,15 +23,9 @@ import org.jackhuang.hmcl.task.TaskResult; public class MaintainTask extends TaskResult { private final Version version; - private final String id; public MaintainTask(Version version) { - this(version, ID); - } - - public MaintainTask(Version version, String id) { this.version = version; - this.id = id; } @Override @@ -76,11 +70,4 @@ public class MaintainTask extends TaskResult { return builder.build(); } - - @Override - public String getId() { - return id; - } - - public static final String ID = "version"; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/VersionList.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/VersionList.java index e00a8abc5..da4cc8780 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/VersionList.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/VersionList.java @@ -74,7 +74,7 @@ public abstract class VersionList { } public Task loadAsync(DownloadProvider downloadProvider) { - return Task.ofThen(variables -> { + return Task.ofThen(() -> { lock.readLock().lock(); boolean loaded; @@ -88,7 +88,7 @@ public abstract class VersionList { } public Task loadAsync(String gameVersion, DownloadProvider downloadProvider) { - return Task.ofThen(variables -> { + return Task.ofThen(() -> { lock.readLock().lock(); boolean loaded; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java index 2843c56b3..c29de94a5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeInstallTask.java @@ -82,11 +82,6 @@ public final class ForgeInstallTask extends TaskResult { return Collections.singleton(dependency); } - @Override - public String getId() { - return "version"; - } - @Override public boolean isRelyingOnDependencies() { return false; 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 8dc7f8a9f..fe1250485 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 @@ -89,11 +89,6 @@ public class ForgeNewInstallTask extends TaskResult { return dependencies; } - @Override - public String getId() { - return "version"; - } - @Override public boolean doPreExecute() { return true; 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 fca62abec..1c961c91d 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 @@ -49,11 +49,6 @@ public class ForgeOldInstallTask extends TaskResult { return dependencies; } - @Override - public String getId() { - return "version"; - } - @Override public boolean doPreExecute() { return true; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibrariesUniqueTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibrariesUniqueTask.java index fcc33b137..23d586c1e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibrariesUniqueTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibrariesUniqueTask.java @@ -33,15 +33,9 @@ import java.util.stream.Collectors; public class LibrariesUniqueTask extends TaskResult { private final Version version; - private final String id; public LibrariesUniqueTask(Version version) { - this(version, "version"); - } - - public LibrariesUniqueTask(Version version, String id) { this.version = version; - this.id = id; } @Override @@ -99,9 +93,4 @@ public class LibrariesUniqueTask extends TaskResult { setResult(version.setLibraries(multimap.values().stream().sorted().collect(Collectors.toList()))); } - - @Override - public String getId() { - return id; - } } 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 fecb40e3f..311ffaa1a 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 @@ -225,16 +225,16 @@ public class LibraryDownloadTask extends Task { int x = decompressed.length; int len = decompressed[(x - 8)] & 0xFF | (decompressed[(x - 7)] & 0xFF) << 8 | (decompressed[(x - 6)] & 0xFF) << 16 | (decompressed[(x - 5)] & 0xFF) << 24; - File temp = FileUtils.createTempFile("minecraft", ".pack"); + Path temp = Files.createTempFile("minecraft", ".pack"); byte[] checksums = Arrays.copyOfRange(decompressed, decompressed.length - len - 8, decompressed.length - 8); - OutputStream out = new FileOutputStream(temp); - out.write(decompressed, 0, decompressed.length - len - 8); - out.close(); + try (OutputStream out = Files.newOutputStream(temp)) { + out.write(decompressed, 0, decompressed.length - len - 8); + } try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) { - Pack200.newUnpacker().unpack(temp, jos); + Pack200.newUnpacker().unpack(temp.toFile(), jos); JarEntry checksumsFile = new JarEntry("checksums.sha1"); checksumsFile.setTime(0L); @@ -243,6 +243,6 @@ public class LibraryDownloadTask extends Task { jos.closeEntry(); } - temp.delete(); + Files.delete(temp); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java index d60b52927..d24bc5a6a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonDownloadTask.java @@ -22,6 +22,7 @@ import org.jackhuang.hmcl.download.RemoteVersion; import org.jackhuang.hmcl.download.VersionList; import org.jackhuang.hmcl.task.GetTask; import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.util.io.NetworkUtils; import java.util.Collection; @@ -32,7 +33,7 @@ import java.util.List; * * @author huangyuhui */ -public final class VersionJsonDownloadTask extends Task { +public final class VersionJsonDownloadTask extends TaskResult { private final String gameVersion; private final DefaultDependencyManager dependencyManager; private final List dependents = new LinkedList<>(); @@ -65,8 +66,6 @@ public final class VersionJsonDownloadTask extends Task { RemoteVersion remoteVersion = gameVersionList.getVersions(gameVersion).stream().findFirst() .orElseThrow(() -> new IllegalStateException("Cannot find specific version " + gameVersion + " in remote repository")); String jsonURL = dependencyManager.getDownloadProvider().injectURL(remoteVersion.getUrl()); - dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL), ID)); + dependencies.add(new GetTask(NetworkUtils.toURL(jsonURL)).storeTo(this::setResult)); } - - public static final String ID = "raw_version_json"; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java index 2cc57b3cb..9c3793fc4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/VersionJsonSaveTask.java @@ -19,7 +19,7 @@ package org.jackhuang.hmcl.download.game; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Version; -import org.jackhuang.hmcl.task.Task; +import org.jackhuang.hmcl.task.TaskResult; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; @@ -30,7 +30,7 @@ import java.io.File; * * @author huangyuhui */ -public final class VersionJsonSaveTask extends Task { +public final class VersionJsonSaveTask extends TaskResult { private final DefaultGameRepository repository; private final Version version; @@ -46,6 +46,7 @@ public final class VersionJsonSaveTask extends Task { this.version = version; setSignificance(TaskSignificance.MODERATE); + setResult(version); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java index f62c77c17..558714f95 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/liteloader/LiteLoaderInstallTask.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.download.liteloader; import org.jackhuang.hmcl.download.DefaultDependencyManager; -import org.jackhuang.hmcl.download.game.GameLibrariesTask; import org.jackhuang.hmcl.game.LibrariesDownloadInfo; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.game.LibraryDownloadInfo; @@ -61,11 +60,6 @@ public final class LiteLoaderInstallTask extends TaskResult { return dependencies; } - @Override - public String getId() { - return "version"; - } - @Override public void execute() { Library library = new Library( 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 e9c10ea6d..716ddfad4 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 @@ -59,11 +59,6 @@ public final class OptiFineInstallTask extends TaskResult { return dependencies; } - @Override - public String getId() { - return "version"; - } - @Override public boolean isRelyingOnDependencies() { return false; 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 f20eda3cf..39ee352e3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepository.java @@ -168,8 +168,8 @@ public class DefaultGameRepository implements GameRepository { versions.remove(id); - if (FileUtils.isMovingToTrashSupported()) { - return FileUtils.moveToTrash(removedFile); + if (FileUtils.isMovingToTrashSupported() && FileUtils.moveToTrash(removedFile)) { + return true; } // remove json files first to ensure HMCL will not recognize this folder as a valid version. diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java index 7880b47ff..e0f4105ea 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java @@ -23,7 +23,7 @@ import com.google.gson.reflect.TypeToken; import org.jackhuang.hmcl.util.*; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; -import org.jackhuang.hmcl.util.io.IOUtils; +import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; import java.io.IOException; @@ -119,7 +119,7 @@ public final class ForgeModMetadata { Path mcmod = fs.getPath("mcmod.info"); if (Files.notExists(mcmod)) throw new IOException("File " + modFile + " is not a Forge mod."); - List modList = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(Files.newInputStream(mcmod)), + List modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), new TypeToken>() { }.getType()); if (modList == null || modList.isEmpty()) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java index b9c6b06ea..c453ce859 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/MultiMCModpackInstallTask.java @@ -156,7 +156,7 @@ public final class MultiMCModpackInstallTask extends Task { } } - dependencies.add(new MaintainTask(version).then(var -> new VersionJsonSaveTask(repository, var.get(MaintainTask.ID)))); + dependencies.add(new MaintainTask(version).thenTaskResult(maintainedVersion -> new VersionJsonSaveTask(repository, maintainedVersion))); dependencies.add(new MinecraftInstanceTask<>(zipFile, modpack.getEncoding(), "/" + manifest.getName() + "/minecraft", manifest, MODPACK_TYPE, repository.getModpackConfiguration(name))); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CoupleTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CoupleTask.java index c32705491..f9d87efe7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CoupleTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CoupleTask.java @@ -18,57 +18,53 @@ package org.jackhuang.hmcl.task; import org.jackhuang.hmcl.util.AutoTypingMap; -import org.jackhuang.hmcl.util.function.ExceptionalFunction; +import org.jackhuang.hmcl.util.function.ExceptionalSupplier; import java.util.Collection; import java.util.Collections; -import java.util.LinkedList; -import java.util.List; /** * A task that combines two tasks and make sure [pred] runs before succ. * * @author huangyuhui */ -final class CoupleTask

extends Task { +final class CoupleTask extends Task { private final boolean relyingOnDependents; - private final Collection dependents; - private final List dependencies = new LinkedList<>(); - private final ExceptionalFunction, Task, ?> succ; + private final Task pred; + private Task succ; + private final ExceptionalSupplier supplier; /** * A task that combines two tasks and make sure pred runs before succ. * - * @param pred the task that runs before succ. - * @param succ a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred. + * @param pred the task that runs before supplier. + * @param supplier a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred. * @param relyingOnDependents true if this task chain will be broken when task pred fails. */ - public CoupleTask(P pred, ExceptionalFunction, Task, ?> succ, boolean relyingOnDependents) { - this.dependents = pred == null ? Collections.emptySet() : Collections.singleton(pred); - this.succ = succ; + CoupleTask(Task pred, ExceptionalSupplier supplier, boolean relyingOnDependents) { + this.pred = pred; + this.supplier = supplier; this.relyingOnDependents = relyingOnDependents; setSignificance(TaskSignificance.MODERATE); - setName(succ.toString()); + setName(supplier.toString()); } @Override public void execute() throws Exception { - setName(succ.toString()); - Task task = succ.apply(getVariables()); - if (task != null) - dependencies.add(task); + setName(supplier.toString()); + succ = supplier.get(); } @Override public Collection getDependents() { - return dependents; + return pred == null ? Collections.emptySet() : Collections.singleton(pred); } @Override public Collection getDependencies() { - return dependencies; + return succ == null ? Collections.emptySet() : Collections.singleton(succ); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index 92ecbe580..7829d02b0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -33,6 +33,7 @@ import java.io.RandomAccessFile; import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import java.security.MessageDigest; import java.util.Optional; @@ -212,7 +213,7 @@ public class FileDownloadTask extends Task { break; } - File temp = null; + Path temp = null; try { updateProgress(0); @@ -242,15 +243,15 @@ public class FileDownloadTask extends Task { if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) throw new IOException("Could not make directory " + file.getAbsoluteFile().getParent()); - temp = FileUtils.createTempFile(); - rFile = new RandomAccessFile(temp, "rw"); + temp = Files.createTempFile(null, null); + rFile = new RandomAccessFile(temp.toFile(), "rw"); MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest(); stream = con.getInputStream(); int lastDownloaded = 0, downloaded = 0; long lastTime = System.currentTimeMillis(); - byte buffer[] = new byte[IOUtils.DEFAULT_BUFFER_SIZE]; + byte[] buffer = new byte[IOUtils.DEFAULT_BUFFER_SIZE]; while (true) { if (Thread.interrupted()) { Thread.currentThread().interrupt(); @@ -283,16 +284,15 @@ public class FileDownloadTask extends Task { // Restore temp file to original name. if (Thread.interrupted()) { - temp.delete(); + temp.toFile().delete(); Thread.currentThread().interrupt(); break; } else { - if (file.exists() && !file.delete()) - throw new IOException("Unable to delete existent file " + file); + Files.deleteIfExists(file.toPath()); if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile())) throw new IOException("Unable to make parent directory " + file); try { - FileUtils.moveFile(temp, file); + FileUtils.moveFile(temp.toFile(), file); } catch (Exception e) { throw new IOException("Unable to move temp file from " + temp + " to " + file, e); } @@ -321,7 +321,7 @@ public class FileDownloadTask extends Task { return; } catch (IOException | IllegalStateException e) { if (temp != null) - temp.delete(); + temp.toFile().delete(); exception = e; } finally { closeFiles(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FinalizedCallback.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FinalizedCallback.java deleted file mode 100644 index 539b6f11d..000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FinalizedCallback.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2019 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.task; - -import org.jackhuang.hmcl.util.AutoTypingMap; - -public interface FinalizedCallback { - void execute(AutoTypingMap variables, boolean isDependentsSucceeded, Exception exception) throws Exception; -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FinalizedTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FinalizedTask.java index d0b6caef4..a060d0d7c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FinalizedTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FinalizedTask.java @@ -37,13 +37,12 @@ final class FinalizedTask extends Task { * @param pred the task that runs before succ. * @param callback a callback that returns the task runs after pred, succ will be executed asynchronously. You can do something that relies on the result of pred. */ - public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback, String name) { + public FinalizedTask(Task pred, Scheduler scheduler, FinalizedCallback callback) { this.pred = pred; this.scheduler = scheduler; this.callback = callback; setSignificance(TaskSignificance.MODERATE); - setName(name); } @Override @@ -53,7 +52,7 @@ final class FinalizedTask extends Task { @Override public void execute() throws Exception { - callback.execute(getVariables(), isDependentsSucceeded(), pred.getLastException()); + callback.execute(isDependentsSucceeded(), pred.getLastException()); if (!isDependentsSucceeded()) throw new SilentException(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/GetTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/GetTask.java index d391b65df..376d3914a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/GetTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/GetTask.java @@ -72,11 +72,6 @@ public final class GetTask extends TaskResult { return Schedulers.io(); } - @Override - public String getId() { - return id; - } - public GetTask setCacheRepository(CacheRepository repository) { this.repository = repository; return this; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTask.java index 6386aba09..f8bd7f3d9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTask.java @@ -17,8 +17,7 @@ */ package org.jackhuang.hmcl.task; -import org.jackhuang.hmcl.util.AutoTypingMap; -import org.jackhuang.hmcl.util.function.ExceptionalConsumer; +import org.jackhuang.hmcl.util.function.ExceptionalRunnable; /** * @@ -26,16 +25,16 @@ import org.jackhuang.hmcl.util.function.ExceptionalConsumer; */ class SimpleTask extends Task { - private final ExceptionalConsumer, ?> consumer; + private final ExceptionalRunnable closure; private final Scheduler scheduler; - public SimpleTask(String name, ExceptionalConsumer, ?> consumer, Scheduler scheduler) { - this.consumer = consumer; + public SimpleTask(String name, ExceptionalRunnable closure, Scheduler scheduler) { + this.closure = closure; this.scheduler = scheduler; if (name == null) { setSignificance(TaskSignificance.MINOR); - setName(consumer.toString()); + setName(closure.toString()); } else { setName(name); } @@ -48,6 +47,6 @@ class SimpleTask extends Task { @Override public void execute() throws Exception { - consumer.accept(getVariables()); + closure.run(); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTaskResult.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTaskResult.java index 393a3272c..22111f654 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTaskResult.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/SimpleTaskResult.java @@ -17,24 +17,30 @@ */ package org.jackhuang.hmcl.task; +import org.jackhuang.hmcl.util.AutoTypingMap; +import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.function.ExceptionalSupplier; -public final class SimpleTaskResult extends TaskResult { - private final String id; - private final ExceptionalSupplier supplier; +import java.util.concurrent.Callable; - public SimpleTaskResult(String id, ExceptionalSupplier supplier) { - this.id = id; - this.supplier = supplier; +/** + * + * @author huangyuhui + */ +class SimpleTaskResult extends TaskResult { + + private final Callable callable; + + public SimpleTaskResult(Callable callable) { + this.callable = callable; + } + + public SimpleTaskResult(ExceptionalSupplier supplier) { + this.callable = supplier.toCallable(); } @Override public void execute() throws Exception { - setResult(supplier.get()); - } - - @Override - public String getId() { - return id; + setResult(callable.call()); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index 631c8ae55..cede8eebc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -23,13 +23,12 @@ import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyStringProperty; import javafx.beans.property.ReadOnlyStringWrapper; import org.jackhuang.hmcl.event.EventManager; -import org.jackhuang.hmcl.util.AutoTypingMap; import org.jackhuang.hmcl.util.InvocationDispatcher; import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.ReflectionHelper; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; -import org.jackhuang.hmcl.util.function.ExceptionalFunction; import org.jackhuang.hmcl.util.function.ExceptionalRunnable; +import org.jackhuang.hmcl.util.function.ExceptionalSupplier; import java.util.Collection; import java.util.Collections; @@ -134,16 +133,6 @@ public abstract class Task { return this; } - private AutoTypingMap variables = null; - - public AutoTypingMap getVariables() { - return variables; - } - - void setVariables(AutoTypingMap variables) { - this.variables = variables; - } - public boolean doPreExecute() { return false; } @@ -280,16 +269,8 @@ public abstract class Task { new TaskExecutor(then(subscriber)).start(); } - public final void subscribe(Scheduler scheduler, ExceptionalConsumer, ?> closure) { - subscribe(of(scheduler, closure)); - } - public final void subscribe(Scheduler scheduler, ExceptionalRunnable closure) { - subscribe(of(scheduler, ExceptionalConsumer.fromRunnable(closure))); - } - - public final void subscribe(ExceptionalConsumer, ?> closure) { - subscribe(of(closure)); + subscribe(of(scheduler, closure)); } public final void subscribe(ExceptionalRunnable closure) { @@ -300,16 +281,41 @@ public abstract class Task { return then(convert(b)); } - public final Task then(ExceptionalFunction, Task, ?> b) { - return new CoupleTask<>(this, b, true); + public final Task then(ExceptionalSupplier b) { + return new CoupleTask(this, b, true); + } + + public final TaskResult thenResult(Callable supplier) { + return thenTaskResult(() -> Task.ofResult(supplier)); + } + + public final TaskResult thenTaskResult(ExceptionalSupplier, ?> taskSupplier) { + return new TaskResult() { + TaskResult then; + + @Override + public Collection getDependents() { + return Collections.singleton(Task.this); + } + + @Override + public void execute() throws Exception { + then = taskSupplier.get().storeTo(this::setResult); + } + + @Override + public Collection getDependencies() { + return then == null ? Collections.emptyList() : Collections.singleton(then); + } + }; } public final Task with(Task b) { return with(convert(b)); } - public final Task with(ExceptionalFunction, Task, E> b) { - return new CoupleTask<>(this, b, false); + public final Task with(ExceptionalSupplier b) { + return new CoupleTask(this, b, false); } public final Task finalized(FinalizedCallback b) { @@ -317,15 +323,16 @@ public abstract class Task { } public final Task finalized(Scheduler scheduler, FinalizedCallback b) { - return new FinalizedTask(this, scheduler, b, ReflectionHelper.getCaller().toString()); + return new FinalizedTask(this, scheduler, b).setName(getCaller()); } - public final Task finalized(Scheduler scheduler, ExceptionalConsumer, T> success, ExceptionalConsumer failure) { - return finalized(scheduler, (variables, isDependentsSucceeded, exception) -> { + // T, K here is necessary, or javac cannot infer type of failure + public final Task finalized(Scheduler scheduler, ExceptionalRunnable success, ExceptionalConsumer failure) { + return finalized(scheduler, (isDependentsSucceeded, exception) -> { if (isDependentsSucceeded) { if (success != null) try { - success.accept(variables); + success.run(); } catch (Exception e) { Logging.LOG.log(Level.WARNING, "Failed to execute " + success, e); if (failure != null) @@ -338,58 +345,38 @@ public abstract class Task { }); } - public static Task of(String name, ExceptionalRunnable runnable) { - return of(name, ExceptionalConsumer.fromRunnable(runnable)); - } - - public static Task of(ExceptionalRunnable runnable) { - return of(ExceptionalConsumer.fromRunnable(runnable)); - } - - public static Task of(String name, ExceptionalConsumer, ?> closure) { - return of(name, Schedulers.defaultScheduler(), closure); - } - - public static Task of(ExceptionalConsumer, ?> closure) { + public static Task of(ExceptionalRunnable closure) { return of(Schedulers.defaultScheduler(), closure); } - public static Task of(String name, Scheduler scheduler, ExceptionalConsumer, ?> closure) { - return new SimpleTask(name, closure, scheduler); - } - - public static Task of(Scheduler scheduler, ExceptionalConsumer, ?> closure) { - return of(ReflectionHelper.getCaller().toString(), scheduler, closure); - } - - public static Task of(String name, Scheduler scheduler, ExceptionalRunnable closure) { - return new SimpleTask(name, ExceptionalConsumer.fromRunnable(closure), scheduler); + public static Task of(String name, ExceptionalRunnable closure) { + return of(name, Schedulers.defaultScheduler(), closure); } public static Task of(Scheduler scheduler, ExceptionalRunnable closure) { - return of(ReflectionHelper.getCaller().toString(), scheduler, closure); + return of(getCaller(), scheduler, closure); } - public static Task ofThen(ExceptionalFunction, Task, ?> b) { - return new CoupleTask<>(null, b, true); + public static Task of(String name, Scheduler scheduler, ExceptionalRunnable closure) { + return new SimpleTask(name, closure, scheduler); + } + + public static Task ofThen(ExceptionalSupplier b) { + return new CoupleTask(null, b, true); } public static TaskResult ofResult(Callable callable) { - return ofResult("", callable); + return ofResult(getCaller(), callable); } - public static TaskResult ofResult(String id, Callable callable) { - return new TaskCallable<>(id, callable); + public static TaskResult ofResult(String name, Callable callable) { + return new SimpleTaskResult<>(callable).setName(name); } - public static TaskResult ofResult(String id, ExceptionalFunction, V, ?> closure) { - return new TaskCallable<>(id, closure); - } - - private static ExceptionalFunction, Task, ?> convert(Task t) { - return new ExceptionalFunction, Task, Exception>() { + private static ExceptionalSupplier convert(Task t) { + return new ExceptionalSupplier() { @Override - public Task apply(AutoTypingMap autoTypingMap) { + public Task get() { return t; } @@ -421,4 +408,12 @@ public abstract class Task { SUCCEEDED, FAILED } + + public interface FinalizedCallback { + void execute(boolean isDependentsSucceeded, Exception exception) throws Exception; + } + + static String getCaller() { + return ReflectionHelper.getCaller(packageName -> !"org.jackhuang.hmcl.task".equals(packageName)).toString(); + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskCallable.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskCallable.java deleted file mode 100644 index 1b7e85432..000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskCallable.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2019 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.task; - -import org.jackhuang.hmcl.util.AutoTypingMap; -import org.jackhuang.hmcl.util.function.ExceptionalFunction; - -import java.util.concurrent.Callable; - -/** - * - * @author huangyuhui - */ -class TaskCallable extends TaskResult { - - private final String id; - private final ExceptionalFunction, V, ?> callable; - - public TaskCallable(String id, Callable callable) { - this(id, variables -> callable.call()); - } - - public TaskCallable(String id, ExceptionalFunction, V, ?> callable) { - this.id = id; - this.callable = callable; - } - - @Override - public String getId() { - return id; - } - - @Override - public void execute() throws Exception { - setResult(callable.apply(getVariables())); - } -} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java index 074e1c161..12587b715 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskExecutor.java @@ -38,7 +38,6 @@ public final class TaskExecutor { private Exception lastException; private final AtomicInteger totTask = new AtomicInteger(0); private final ConcurrentLinkedQueue> workerQueue = new ConcurrentLinkedQueue<>(); - private final AutoTypingMap variables = new AutoTypingMap<>(new HashMap<>()); private Scheduler scheduler = Schedulers.newThread(); public TaskExecutor(Task task) { @@ -147,8 +146,6 @@ public final class TaskExecutor { boolean flag = false; try { - task.setVariables(variables); - if (task.doPreExecute()) { try { task.getScheduler().schedule(task::preExecute).get(); @@ -186,11 +183,6 @@ public final class TaskExecutor { task.setState(Task.TaskState.EXECUTED); } - if (task instanceof TaskResult) { - TaskResult taskResult = (TaskResult) task; - variables.set(taskResult.getId(), taskResult.getResult()); - } - Collection dependencies = task.getDependencies(); boolean doDependenciesSucceeded = executeTasks(dependencies); Exception dependenciesException = dependencies.stream().map(Task::getLastException).filter(Objects::nonNull).findAny().orElse(null); @@ -238,8 +230,6 @@ public final class TaskExecutor { } task.onDone().fireEvent(new TaskEvent(this, task, true)); taskListeners.forEach(it -> it.onFailed(task, e)); - } finally { - task.setVariables(null); } task.setState(flag ? Task.TaskState.SUCCEEDED : Task.TaskState.FAILED); return flag; @@ -249,10 +239,6 @@ public final class TaskExecutor { return totTask.get(); } - public AutoTypingMap getVariables() { - return variables; - } - private class Invoker implements ExceptionalRunnable { private final Task task; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskResult.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskResult.java index 533fead55..5f9185ae0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskResult.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/TaskResult.java @@ -17,12 +17,12 @@ */ package org.jackhuang.hmcl.task; -import org.jackhuang.hmcl.util.ReflectionHelper; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.function.ExceptionalFunction; import java.util.Collection; import java.util.Collections; +import java.util.function.Consumer; /** * A task that has a result. @@ -32,6 +32,13 @@ import java.util.Collections; public abstract class TaskResult extends Task { private V result; + private Consumer resultConsumer; + + @Override + public TaskResult setName(String name) { + super.setName(name); + return this; + } public V getResult() { return result; @@ -39,41 +46,78 @@ public abstract class TaskResult extends Task { public void setResult(V result) { this.result = result; + if (resultConsumer != null) + resultConsumer.accept(result); } - public abstract String getId(); + public TaskResult storeTo(Consumer resultConsumer) { + this.resultConsumer = resultConsumer; + return this; + } + + public TaskResult thenTaskResult(ExceptionalFunction, E> taskSupplier) { + return new TaskResult() { + TaskResult then; + + @Override + public Collection getDependents() { + return Collections.singleton(TaskResult.this); + } + + @Override + public void execute() throws Exception { + then = taskSupplier.apply(TaskResult.this.getResult()).storeTo(this::setResult); + } + + @Override + public Collection getDependencies() { + return then == null ? Collections.emptyList() : Collections.singleton(then); + } + }; + } + + public Task then(ExceptionalFunction taskSupplier) { + return new CoupleTask(this, () -> taskSupplier.apply(getResult()), true); + } public TaskResult thenResult(ExceptionalFunction task) { return thenResult(Schedulers.defaultScheduler(), task); } public TaskResult thenResult(Scheduler scheduler, ExceptionalFunction task) { - return thenResult(ReflectionHelper.getCaller().toString(), scheduler, task); + return thenResult(getCaller(), scheduler, task); } - public TaskResult thenResult(String id, Scheduler scheduler, ExceptionalFunction task) { - return new Subtask<>(id, scheduler, task); + public TaskResult thenResult(String name, Scheduler scheduler, ExceptionalFunction task) { + return new Subtask<>(name, scheduler, task); } - public Task finalizedResult(Scheduler scheduler, ExceptionalConsumer success, ExceptionalConsumer failure) { - return finalized(scheduler, variables -> success.accept(getResult()), failure); + // stupid javac stop us from renaming thenVoid to thenResult + public Task thenVoid(ExceptionalConsumer task) { + return thenVoid(Schedulers.defaultScheduler(), task); } - public Task finalizedResult(Scheduler scheduler, FinalizedCallback callback) { - return new FinalizedTask(this, scheduler, - (variables, isDependentsSucceeded, exception) -> callback.execute(getResult(), isDependentsSucceeded, exception), - ReflectionHelper.getCaller().toString()); + public Task thenVoid(Scheduler scheduler, ExceptionalConsumer task) { + return new CoupleTask(this, () -> Task.of(scheduler, () -> task.accept(getResult())), true); + } + + public Task finalized(Scheduler scheduler, ExceptionalConsumer success, ExceptionalConsumer failure) { + return finalized(scheduler, () -> success.accept(getResult()), failure); + } + + public Task finalized(Scheduler scheduler, FinalizedCallback callback) { + return finalized(scheduler, ((isDependentsSucceeded, exception) -> callback.execute(getResult(), isDependentsSucceeded, exception))); } private class Subtask extends TaskResult { - private final String id; private final Scheduler scheduler; private final ExceptionalFunction callable; - public Subtask(String id, Scheduler scheduler, ExceptionalFunction callable) { - this.id = id; + public Subtask(String name, Scheduler scheduler, ExceptionalFunction callable) { this.scheduler = scheduler; this.callable = callable; + + setName(name); } @Override @@ -81,11 +125,6 @@ public abstract class TaskResult extends Task { return Collections.singleton(TaskResult.this); } - @Override - public String getId() { - return id; - } - @Override public Scheduler getScheduler() { return scheduler; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java index aa497f936..12e24507e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ReflectionHelper.java @@ -17,17 +17,28 @@ */ package org.jackhuang.hmcl.util; +import java.util.function.Predicate; + /** * * @author huangyuhui */ public final class ReflectionHelper { - public static StackTraceElement getCaller() { + /** + * Get caller, this method is caller sensitive. + * @param packageFilter returns false if we consider the given package is internal calls, not the caller + * @return the caller, method name, source file, line number + */ + public static StackTraceElement getCaller(Predicate packageFilter) { StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + // element[0] is Thread.currentThread().getStackTrace() + // element[1] is ReflectionHelper.getCaller(packageFilter) + // so element[2] is caller of this method. StackTraceElement caller = elements[2]; for (int i = 3; i < elements.length; ++i) { - if (!caller.getClassName().equals(elements[i].getClassName())) + if (packageFilter.test(StringUtils.substringBeforeLast(elements[i].getClassName(), '.')) && + !caller.getClassName().equals(elements[i].getClassName())) return elements[i]; } return caller; 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 52fe65a8f..a38e53f2f 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 @@ -116,14 +116,43 @@ public final class FileUtils { return new String(Files.readAllBytes(file), 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, text, UTF_8); } + /** + * Write plain text to file. + * + * 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 + * @param charset the charset to use for encoding + * @throws IOException if an I/O error occurs + */ public static void writeText(File file, String text, Charset charset) throws IOException { writeBytes(file, text.getBytes(charset)); } + /** + * 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 array the data being written to file + * @throws IOException if an I/O error occurs + */ public static void writeBytes(File file, byte[] array) throws IOException { Files.createDirectories(file.toPath().getParent()); Files.write(file.toPath(), array); @@ -153,6 +182,14 @@ public final class FileUtils { } } + /** + * Copy directory. + * Paths of all files relative to source directory will be the same as the ones relative to destination directory. + * + * @param src the source directory. + * @param dest the destination directory, which will be created if not existing. + * @throws IOException if an I/O error occurs. + */ public static void copyDirectory(Path src, Path dest) throws IOException { Files.walkFileTree(src, new SimpleFileVisitor(){ @Override @@ -173,6 +210,20 @@ public final class FileUtils { }); } + /** + * Move file to trash. + * + * This method is only implemented in Java 9. Please check we are using Java 9 by invoking isMovingToTrashSupported. + * Example: + *

{@code
+     * if (FileUtils.isMovingToTrashSupported()) {
+     *     FileUtils.moveToTrash(file);
+     * }
+     * }
+ * @param file the file being moved to trash. + * @see FileUtils#isMovingToTrashSupported() + * @return false if moveToTrash does not exist, or platform does not support Desktop.Action.MOVE_TO_TRASH + */ public static boolean moveToTrash(File file) { try { java.awt.Desktop desktop = java.awt.Desktop.getDesktop(); @@ -314,20 +365,4 @@ public final class FileUtils { result.add(it); return result; } - - public static File createTempFile() throws IOException { - return createTempFile("tmp"); - } - - public static File createTempFile(String prefix) throws IOException { - return createTempFile(prefix, null); - } - - public static File createTempFile(String prefix, String suffix) throws IOException { - return createTempFile(prefix, suffix, null); - } - - public static File createTempFile(String prefix, String suffix, File directory) throws IOException { - return File.createTempFile(prefix, suffix, directory); - } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java index e5421a2c3..25d085c0e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/IOUtils.java @@ -21,8 +21,9 @@ import java.io.*; import java.nio.charset.Charset; /** + * This utility class consists of some util methods operating on InputStream/OutputStream. * - * @author huang + * @author huangyuhui */ public final class IOUtils { @@ -31,12 +32,26 @@ public final class IOUtils { public static final int DEFAULT_BUFFER_SIZE = 8 * 1024; + /** + * Read all bytes to a buffer from given input stream. The stream will not be closed. + * + * @param stream the InputStream being read. + * @return all bytes read from the stream + * @throws IOException if an I/O error occurs. + */ public static byte[] readFullyWithoutClosing(InputStream stream) throws IOException { ByteArrayOutputStream result = new ByteArrayOutputStream(); copyTo(stream, result); return result.toByteArray(); } + /** + * Read all bytes to a buffer from given input stream, and close the input stream finally. + * + * @param stream the InputStream being read, closed finally. + * @return all bytes read from the stream + * @throws IOException if an I/O error occurs. + */ public static ByteArrayOutputStream readFully(InputStream stream) throws IOException { try (InputStream is = stream) { ByteArrayOutputStream result = new ByteArrayOutputStream();