重构 FutureCallback (#5268)
This commit is contained in:
@@ -325,13 +325,13 @@ public final class LauncherHelper {
|
||||
message = i18n("launch.failed.command_too_long");
|
||||
} else if (ex instanceof ExecutionPolicyLimitException) {
|
||||
Controllers.prompt(new PromptDialogPane.Builder(i18n("launch.failed.execution_policy"),
|
||||
(result, resolve, reject) -> {
|
||||
(result, handler) -> {
|
||||
if (CommandBuilder.setExecutionPolicy()) {
|
||||
LOG.info("Set the ExecutionPolicy for the scope 'CurrentUser' to 'RemoteSigned'");
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
} else {
|
||||
LOG.warning("Failed to set ExecutionPolicy");
|
||||
reject.accept(i18n("launch.failed.execution_policy.failed_to_set"));
|
||||
handler.reject(i18n("launch.failed.execution_policy.failed_to_set"));
|
||||
}
|
||||
})
|
||||
.addQuestion(new PromptDialogPane.Builder.HintQuestion(i18n("launch.failed.execution_policy.hint")))
|
||||
|
||||
@@ -72,13 +72,19 @@ public class InputDialogPane extends JFXDialogLayout implements DialogAware {
|
||||
acceptButton.setOnAction(e -> {
|
||||
acceptPane.showSpinner();
|
||||
|
||||
onResult.call(textField.getText(), () -> {
|
||||
acceptPane.hideSpinner();
|
||||
future.complete(textField.getText());
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}, msg -> {
|
||||
acceptPane.hideSpinner();
|
||||
lblCreationWarning.setText(msg);
|
||||
onResult.call(textField.getText(), new FutureCallback.ResultHandler() {
|
||||
@Override
|
||||
public void resolve() {
|
||||
acceptPane.hideSpinner();
|
||||
future.complete(textField.getText());
|
||||
fireEvent(new DialogCloseEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reject(String reason) {
|
||||
acceptPane.hideSpinner();
|
||||
lblCreationWarning.setText(reason);
|
||||
}
|
||||
});
|
||||
});
|
||||
textField.setOnAction(event -> acceptButton.fire());
|
||||
|
||||
@@ -117,11 +117,17 @@ public class PromptDialogPane extends DialogPane {
|
||||
protected void onAccept() {
|
||||
setLoading();
|
||||
|
||||
builder.callback.call(builder.questions, () -> {
|
||||
future.complete(builder.questions);
|
||||
runInFX(this::onSuccess);
|
||||
}, msg -> {
|
||||
runInFX(() -> onFailure(msg));
|
||||
builder.callback.call(builder.questions, new FutureCallback.ResultHandler() {
|
||||
@Override
|
||||
public void resolve() {
|
||||
future.complete(builder.questions);
|
||||
runInFX(() -> onSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reject(String reason) {
|
||||
runInFX(() -> onFailure(reason));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
|
||||
Path runDirectory = profile.getRepository().hasVersion(version) ? profile.getRepository().getRunDirectory(version) : profile.getRepository().getBaseDirectory();
|
||||
|
||||
Controllers.prompt(i18n("archive.file.name"), (result, resolve, reject) -> {
|
||||
Controllers.prompt(i18n("archive.file.name"), (result, handler) -> {
|
||||
Path dest = runDirectory.resolve(subdirectoryName).resolve(result);
|
||||
|
||||
Controllers.taskDialog(Task.composeAsync(() -> {
|
||||
@@ -152,7 +152,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
||||
Controllers.showToast(i18n("install.success"));
|
||||
}
|
||||
}), i18n("message.downloading"), TaskCancellationAction.NORMAL);
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
}, file.getFile().getFilename(), new Validator(i18n("install.new_game.malformed"), FileUtils::isNameValid));
|
||||
|
||||
}
|
||||
|
||||
@@ -124,37 +124,37 @@ public final class ModpackSelectionPage extends VBox implements WizardPage {
|
||||
}
|
||||
|
||||
private void onChooseRemoteFile() {
|
||||
Controllers.prompt(i18n("modpack.choose.remote.tooltip"), (url, resolve, reject) -> {
|
||||
Controllers.prompt(i18n("modpack.choose.remote.tooltip"), (url, handler) -> {
|
||||
try {
|
||||
if (url.endsWith("server-manifest.json")) {
|
||||
// if urlString ends with .json, we assume that the url is server-manifest.json
|
||||
Controllers.taskDialog(new GetTask(url).whenComplete(Schedulers.javafx(), (result, e) -> {
|
||||
ServerModpackManifest manifest = JsonUtils.fromMaybeMalformedJson(result, ServerModpackManifest.class);
|
||||
if (manifest == null) {
|
||||
reject.accept(i18n("modpack.type.server.malformed"));
|
||||
handler.reject(i18n("modpack.type.server.malformed"));
|
||||
} else if (e == null) {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
controller.getSettings().put(MODPACK_SERVER_MANIFEST, manifest);
|
||||
controller.onNext();
|
||||
} else {
|
||||
reject.accept(e.getMessage());
|
||||
handler.reject(e.getMessage());
|
||||
}
|
||||
}).executor(true), i18n("message.downloading"), TaskCancellationAction.NORMAL);
|
||||
} else {
|
||||
// otherwise we still consider the file as modpack zip file
|
||||
// since casually the url may not ends with ".zip"
|
||||
Path modpack = Files.createTempFile("modpack", ".zip");
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
|
||||
Controllers.taskDialog(
|
||||
new FileDownloadTask(url, modpack)
|
||||
.whenComplete(Schedulers.javafx(), e -> {
|
||||
if (e == null) {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
controller.getSettings().put(MODPACK_FILE, modpack);
|
||||
controller.onNext();
|
||||
} else {
|
||||
reject.accept(e.getMessage());
|
||||
handler.reject(e.getMessage());
|
||||
}
|
||||
}).executor(true),
|
||||
i18n("message.downloading"),
|
||||
@@ -162,7 +162,7 @@ public final class ModpackSelectionPage extends VBox implements WizardPage {
|
||||
);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
reject.accept(e.getMessage());
|
||||
handler.reject(e.getMessage());
|
||||
}
|
||||
}, "", new URLValidator());
|
||||
}
|
||||
|
||||
@@ -245,19 +245,19 @@ public class TerracottaControllerPage extends StackPane {
|
||||
guest.setSubtitle(i18n("terracotta.status.waiting.guest.desc"));
|
||||
guest.setRightIcon(SVG.ARROW_FORWARD);
|
||||
FXUtils.onClicked(guest, () -> {
|
||||
Controllers.prompt(i18n("terracotta.status.waiting.guest.prompt.title"), (code, resolve, reject) -> {
|
||||
Controllers.prompt(i18n("terracotta.status.waiting.guest.prompt.title"), (code, handler) -> {
|
||||
Task<TerracottaState.GuestConnecting> task = TerracottaManager.setGuesting(code);
|
||||
if (task != null) {
|
||||
task.whenComplete(Schedulers.javafx(), (s, e) -> {
|
||||
if (e != null) {
|
||||
reject.accept(i18n("terracotta.status.waiting.guest.prompt.invalid"));
|
||||
handler.reject(i18n("terracotta.status.waiting.guest.prompt.invalid"));
|
||||
} else {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
UI_STATE.set(s);
|
||||
}
|
||||
}).setSignificance(Task.TaskSignificance.MINOR).start();
|
||||
} else {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -163,30 +163,30 @@ public final class SchematicsPage extends ListPageBase<SchematicsPage.Item> impl
|
||||
Controllers.dialog(new InputDialogPane(
|
||||
i18n("schematics.create_directory.prompt"),
|
||||
"",
|
||||
(result, resolve, reject) -> {
|
||||
(result, handler) -> {
|
||||
if (StringUtils.isBlank(result)) {
|
||||
reject.accept(i18n("schematics.create_directory.failed.empty_name"));
|
||||
handler.reject(i18n("schematics.create_directory.failed.empty_name"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.contains("/") || result.contains("\\") || !FileUtils.isNameValid(result)) {
|
||||
reject.accept(i18n("schematics.create_directory.failed.invalid_name"));
|
||||
handler.reject(i18n("schematics.create_directory.failed.invalid_name"));
|
||||
return;
|
||||
}
|
||||
|
||||
Path targetDir = parent.resolve(result);
|
||||
if (Files.exists(targetDir)) {
|
||||
reject.accept(i18n("schematics.create_directory.failed.already_exists"));
|
||||
handler.reject(i18n("schematics.create_directory.failed.already_exists"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Files.createDirectories(targetDir);
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
refresh();
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to create directory: " + targetDir, e);
|
||||
reject.accept(i18n("schematics.create_directory.failed", targetDir));
|
||||
handler.reject(i18n("schematics.create_directory.failed", targetDir));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -129,13 +129,13 @@ public final class Versions {
|
||||
}
|
||||
|
||||
public static CompletableFuture<String> renameVersion(Profile profile, String version) {
|
||||
return Controllers.prompt(i18n("version.manage.rename.message"), (newName, resolve, reject) -> {
|
||||
return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> {
|
||||
if (newName.equals(version)) {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
return;
|
||||
}
|
||||
if (profile.getRepository().renameVersion(version, newName)) {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
profile.getRepository().refreshVersionsAsync()
|
||||
.thenRunAsync(Schedulers.javafx(), () -> {
|
||||
if (profile.getRepository().hasVersion(newName)) {
|
||||
@@ -143,7 +143,7 @@ public final class Versions {
|
||||
}
|
||||
}).start();
|
||||
} else {
|
||||
reject.accept(i18n("version.manage.rename.fail"));
|
||||
handler.reject(i18n("version.manage.rename.fail"));
|
||||
}
|
||||
}, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId),
|
||||
new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version)));
|
||||
@@ -159,16 +159,16 @@ public final class Versions {
|
||||
|
||||
public static void duplicateVersion(Profile profile, String version) {
|
||||
Controllers.prompt(
|
||||
new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, resolve, reject) -> {
|
||||
new PromptDialogPane.Builder(i18n("version.manage.duplicate.prompt"), (res, handler) -> {
|
||||
String newVersionName = ((PromptDialogPane.Builder.StringQuestion) res.get(1)).getValue();
|
||||
boolean copySaves = ((PromptDialogPane.Builder.BooleanQuestion) res.get(2)).getValue();
|
||||
Task.runAsync(() -> profile.getRepository().duplicateVersion(version, newVersionName, copySaves))
|
||||
.thenComposeAsync(profile.getRepository().refreshVersionsAsync())
|
||||
.whenComplete(Schedulers.javafx(), (result, exception) -> {
|
||||
if (exception == null) {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
} else {
|
||||
reject.accept(StringUtils.getStackTrace(exception));
|
||||
handler.reject(StringUtils.getStackTrace(exception));
|
||||
if (!profile.getRepository().versionIdConflicts(newVersionName)) {
|
||||
profile.getRepository().removeVersionFromDisk(newVersionName);
|
||||
}
|
||||
|
||||
@@ -156,18 +156,18 @@ public final class WorldListPage extends ListPageBase<World> implements VersionP
|
||||
// Or too many input dialogs are popped.
|
||||
Task.supplyAsync(() -> new World(zipFile))
|
||||
.whenComplete(Schedulers.javafx(), world -> {
|
||||
Controllers.prompt(i18n("world.name.enter"), (name, resolve, reject) -> {
|
||||
Controllers.prompt(i18n("world.name.enter"), (name, handler) -> {
|
||||
Task.runAsync(() -> world.install(savesDir, name))
|
||||
.whenComplete(Schedulers.javafx(), () -> {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
refresh();
|
||||
}, e -> {
|
||||
if (e instanceof FileAlreadyExistsException)
|
||||
reject.accept(i18n("world.import.failed", i18n("world.import.already_exists")));
|
||||
handler.reject(i18n("world.import.failed", i18n("world.import.already_exists")));
|
||||
else if (e instanceof IOException && e.getCause() instanceof InvalidPathException)
|
||||
reject.accept(i18n("world.import.failed", i18n("install.new_game.malformed")));
|
||||
handler.reject(i18n("world.import.failed", i18n("install.new_game.malformed")));
|
||||
else
|
||||
reject.accept(i18n("world.import.failed", e.getClass().getName() + ": " + e.getLocalizedMessage()));
|
||||
handler.reject(i18n("world.import.failed", e.getClass().getName() + ": " + e.getLocalizedMessage()));
|
||||
}).start();
|
||||
}, world.getWorldName(), new Validator(i18n("install.new_game.malformed"), FileUtils::isNameValid));
|
||||
}, e -> {
|
||||
|
||||
@@ -92,20 +92,20 @@ public final class WorldManageUIUtils {
|
||||
Controllers.dialog(new InputDialogPane(
|
||||
i18n("world.duplicate.prompt"),
|
||||
"",
|
||||
(result, resolve, reject) -> {
|
||||
(result, handler) -> {
|
||||
if (StringUtils.isBlank(result)) {
|
||||
reject.accept(i18n("world.duplicate.failed.empty_name"));
|
||||
handler.reject(i18n("world.duplicate.failed.empty_name"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.contains("/") || result.contains("\\") || !FileUtils.isNameValid(result)) {
|
||||
reject.accept(i18n("world.duplicate.failed.invalid_name"));
|
||||
handler.reject(i18n("world.duplicate.failed.invalid_name"));
|
||||
return;
|
||||
}
|
||||
|
||||
Path targetDir = worldPath.resolveSibling(result);
|
||||
if (Files.exists(targetDir)) {
|
||||
reject.accept(i18n("world.duplicate.failed.already_exists"));
|
||||
handler.reject(i18n("world.duplicate.failed.already_exists"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ public final class WorldManageUIUtils {
|
||||
}
|
||||
).whenComplete(Schedulers.javafx(), (throwable) -> {
|
||||
if (throwable == null) {
|
||||
resolve.run();
|
||||
handler.resolve();
|
||||
} else {
|
||||
reject.accept(i18n("world.duplicate.failed"));
|
||||
handler.reject(i18n("world.duplicate.failed"));
|
||||
LOG.warning("Failed to duplicate world " + world.getFile(), throwable);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -17,18 +17,23 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface FutureCallback<T> {
|
||||
|
||||
/**
|
||||
* Callback of future, called after future finishes.
|
||||
* This callback gives the feedback whether the result of future is acceptable or not,
|
||||
* if not, giving the reason, and future will be relaunched when necessary.
|
||||
* @param result result of the future
|
||||
* @param resolve accept the result
|
||||
* @param reject reject the result with failure reason
|
||||
*/
|
||||
void call(T result, Runnable resolve, Consumer<String> reject);
|
||||
/// Callback of future, called after future finishes.
|
||||
/// This callback gives the feedback whether the result of future is acceptable or not,
|
||||
/// if not, giving the reason, and future will be relaunched when necessary.
|
||||
///
|
||||
/// @param result result of the future
|
||||
/// @param handler handler to accept or reject the result
|
||||
void call(T result, ResultHandler handler);
|
||||
|
||||
interface ResultHandler {
|
||||
|
||||
/// Accept the result.
|
||||
void resolve();
|
||||
|
||||
/// Reject the result with given reason.
|
||||
void reject(String reason);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user