diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java index 644697e34..b0c081cf8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModUpdatesPage.java @@ -42,8 +42,14 @@ import org.jackhuang.hmcl.ui.construct.PageCloseEvent; import org.jackhuang.hmcl.ui.decorator.DecoratorPage; import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.TaskCancellationAction; +import org.jackhuang.hmcl.util.io.CSVTable; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -100,6 +106,9 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage { actions.setPadding(new Insets(8)); actions.setAlignment(Pos.CENTER_RIGHT); + JFXButton exportListButton = FXUtils.newRaisedButton(i18n("button.export")); + exportListButton.setOnAction(e -> exportList()); + JFXButton nextButton = FXUtils.newRaisedButton(i18n("mods.check_updates.update")); nextButton.setOnAction(e -> updateMods()); @@ -107,7 +116,7 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage { cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent())); onEscPressed(this, cancelButton::fire); - actions.getChildren().setAll(nextButton, cancelButton); + actions.getChildren().setAll(exportListButton, nextButton, cancelButton); setBottom(actions); } @@ -140,6 +149,36 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage { TaskCancellationAction.NORMAL); } + private void exportList() { + Path path = Paths.get("hmcl-mod-update-list-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH-mm-ss")) + ".csv").toAbsolutePath(); + + Controllers.taskDialog(Task.runAsync(() -> { + CSVTable csvTable = CSVTable.createEmpty(); + + csvTable.set(0, 0, "Source File Name"); + csvTable.set(1, 0, "Current Version"); + csvTable.set(2, 0, "Target Version"); + csvTable.set(3, 0, "Update Source"); + + for (int i = 0; i < objects.size(); i++) { + csvTable.set(0, i + 1, objects.get(i).fileName.get()); + csvTable.set(1, i + 1, objects.get(i).currentVersion.get()); + csvTable.set(2, i + 1, objects.get(i).targetVersion.get()); + csvTable.set(3, i + 1, objects.get(i).source.get()); + } + + csvTable.write(Files.newOutputStream(path)); + + FXUtils.showFileInExplorer(path); + }).whenComplete(exception -> { + if (exception == null) { + Controllers.dialog(path.toString(), i18n("message.success")); + } else { + Controllers.dialog("", i18n("message.error"), MessageDialogPane.MessageType.ERROR); + } + }), i18n("button.export"), TaskCancellationAction.NORMAL); + } + @Override public ReadOnlyObjectWrapper stateProperty() { return state; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InfiniteSizeList.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InfiniteSizeList.java new file mode 100644 index 000000000..e5ef84e4e --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InfiniteSizeList.java @@ -0,0 +1,115 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2020 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.util; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; + +public final class InfiniteSizeList extends ArrayList { + private int actualSize; + + public InfiniteSizeList(int initialCapacity) { + super(initialCapacity); + + this.actualSize = 0; + } + + public InfiniteSizeList() { + this.actualSize = 0; + } + + public InfiniteSizeList(@NotNull Collection c) { + super(c); + + this.actualSize = 0; + for (int i = super.size() - 1; i >= 0; i--) { + if (super.get(i) != null) { + actualSize = i + 1; + break; + } + } + } + + @Override + public T get(int index) { + if (index >= super.size()) { + return null; + } + + return super.get(index); + } + + @Override + public T set(int index, T element) { + if (element == null) { // The element is null. + if (index >= super.size()) { + return null; // The element (actually null) is out of the allocated size. + } + + T previous = super.get(index); + if (previous != null) { // !null -> null + super.set(index, null); + + if (index == this.actualSize - 1) { // Recalculate the actualSize. + this.actualSize = 0; + + for (int i = index - 1; i >= 0; i--) { + if (super.get(i) != null) { + this.actualSize = i + 1; + break; + } + } + } + + return previous; + } else { // null -> null + return null; + } + } else { + // The element isn't null. + + if (index >= super.size()) { + allocate0(index); + } + + T previous = super.get(index); + if (previous != null) { // !null -> !null + super.set(index, element); + return previous; + } else { // null -> !null + super.set(index, element); + if (index >= this.actualSize) { + this.actualSize = index + 1; + } + return null; + } + } + } + + private void allocate0(int index) { + super.ensureCapacity(index + 1); + } + + @Override + public int size() { + return actualSize; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CSVTable.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CSVTable.java new file mode 100644 index 000000000..a839cebf1 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/CSVTable.java @@ -0,0 +1,84 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2020 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.util.io; + +import org.jackhuang.hmcl.util.InfiniteSizeList; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.List; + +public final class CSVTable { + private final List> table = new InfiniteSizeList<>(); + + private CSVTable() { + } + + public static CSVTable createEmpty() { + return new CSVTable(); + } + + public String get(int x, int y) { + List row = table.get(y); + if (row == null) { + return null; + } + return row.get(x); + } + + public void set(int x, int y, String txt) { + List row = table.get(y); + if (row == null) { + row = new InfiniteSizeList<>(x); + table.set(y, row); + } + row.set(x, txt); + } + + public void write(OutputStream outputStream) throws IOException { + try (PrintWriter printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter( + outputStream, StandardCharsets.UTF_8)), false + )) { + for (List row : this.table) { + if (row != null) { + for (int j = 0; j < row.size(); j++) { + String txt = row.get(j); + if (txt != null) { + printWriter.write(this.escape(txt)); + } + + if (j != row.size() - 1) { + printWriter.write(','); + } + } + } + + printWriter.write('\n'); + } + } + } + + private String escape(String txt) { + if (!txt.contains("\"") && !txt.contains(",")) { + return txt; + } else { + return "\"" + txt.replace("\"", "\"\"") + "\""; + } + } +}