Skip to content

Commit

Permalink
package renamer improvements (#235)
Browse files Browse the repository at this point in the history
* fix a couple possible crashes in package renamer

* checkstyle

* add a confirmation dialogue for package renaming

* fix test
  • Loading branch information
ix0rai committed Jan 9, 2025
1 parent ead72cc commit a7f5833
Show file tree
Hide file tree
Showing 25 changed files with 259 additions and 106 deletions.
11 changes: 9 additions & 2 deletions enigma-swing/src/main/java/org/quiltmc/enigma/gui/Gui.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ public class Gui {
public final JFileChooser exportJarFileChooser;
public final SearchDialog searchDialog;

public Gui(EnigmaProfile profile, Set<EditableType> editableTypes, boolean visible) {
private final boolean testEnvironment;

public Gui(EnigmaProfile profile, Set<EditableType> editableTypes, boolean testEnvironment) {
this.dockerManager = new DockerManager(this);
this.mainWindow = new MainWindow(this, Enigma.NAME);
this.centerPanel = new JPanel(new BorderLayout());
Expand All @@ -126,14 +128,15 @@ public Gui(EnigmaProfile profile, Set<EditableType> editableTypes, boolean visib
this.connectionStatusLabel = new JLabel();
this.notificationManager = new NotificationManager(this);
this.searchDialog = new SearchDialog(this);
this.testEnvironment = testEnvironment;

this.showsProgressBars = true;

this.setupUi();

LanguageUtil.addListener(this::retranslateUi);

this.mainWindow.setVisible(visible);
this.mainWindow.setVisible(!testEnvironment);
}

private void setupDockers() {
Expand Down Expand Up @@ -273,6 +276,10 @@ public Set<EditableType> getEditableTypes() {
return this.editableTypes;
}

public boolean isTestEnvironment() {
return this.testEnvironment;
}

public void addCrash(Throwable t) {
this.crashHistory.add(t);
this.menuBar.prepareCrashHistoryMenu();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public static void main(String[] args) throws IOException {

KeyBinds.loadConfig();

Gui gui = new Gui(parsedProfile, editables, true);
Gui gui = new Gui(parsedProfile, editables, false);
GuiController controller = gui.getController();

if (options.has("hide-progress-bars")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ private void onRenamePackage(PackageRenamer.Mode mode) {
}
}

String input = JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("popup_menu.class_selector.package_rename.title"), pathString.toString());
String title = switch (mode) {
case MOVE -> I18n.translateFormatted("popup_menu.class_selector.package_rename.move_title", pathString.toString());
case REFACTOR -> I18n.translateFormatted("popup_menu.class_selector.package_rename.rename_title", pathString.toString());
};

String input = JOptionPane.showInputDialog(this.gui.getFrame(), title, pathString.toString());
if (input != null) {
this.createPackageRenamer(mode).renamePackage(pathString.toString(), input);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@
import org.quiltmc.enigma.util.validation.Message;
import org.quiltmc.enigma.util.validation.ValidationContext;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.tree.TreeNode;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -143,7 +150,7 @@ public CompletableFuture<Void> renamePackage(String path, String input) {
String[] oldPackageNames = path.split("/");
String[] newPackageNames = input.split("/");

Map<String, Runnable> renameStack = new HashMap<>();
Map<String, ClassRename> renameStack = new HashMap<>();

return ProgressDialog.runOffThread(this.gui, listener -> {
listener.init(1, I18n.translate("popup_menu.class_selector.package_rename.discovering"));
Expand All @@ -163,10 +170,20 @@ public CompletableFuture<Void> renamePackage(String path, String input) {
}
}

boolean confirmed = false;
int i = 0;
for (var entry : renameStack.entrySet()) {
if (!confirmed && !this.gui.isTestEnvironment()) {
int continueOperation = JOptionPane.showConfirmDialog(this.gui.getFrame(), buildConfirmationPanel(renameStack));
if (continueOperation != JOptionPane.YES_OPTION) {
return;
} else {
confirmed = true;
}
}

listener.step(i, I18n.translateFormatted("popup_menu.class_selector.package_rename.renaming_class", entry.getKey()));
entry.getValue().run();
entry.getValue().executeRename();
i++;
}

Expand All @@ -178,10 +195,54 @@ public CompletableFuture<Void> renamePackage(String path, String input) {
});
}

private void handleNode(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map<String, Runnable> renameStack, TreeNode node) {
private static JPanel buildConfirmationPanel(Map<String, ClassRename> renameStack) {
JPanel panel = new JPanel(new BorderLayout());
int truncationThreshold = 50;

var sampleRenameLines = collectSampleRenames(truncationThreshold, renameStack);
JTextArea text = new JTextArea(multilineify(false, sampleRenameLines));
text.setEditable(false);
JScrollPane sampleRenames = new JScrollPane(text);
sampleRenames.setPreferredSize(new Dimension(ScaleUtil.scale(400), ScaleUtil.scale(100)));

String changesString = I18n.translate("popup_menu.class_selector.package_rename.changes_to_apply") + (sampleRenameLines.length == truncationThreshold ? " (" + I18n.translate("popup_menu.class_selector.package_rename.truncated") + ")" : "");
panel.add(BorderLayout.NORTH, new JLabel(multilineify(true, I18n.translate("popup_menu.class_selector.package_rename.confirm_rename"), changesString)));
panel.add(sampleRenames, BorderLayout.CENTER);

return panel;
}

private static String multilineify(boolean html, String... lines) {
StringBuilder builder = new StringBuilder(html ? "<html>" : "");

for (int i = 0; i < lines.length; i++) {
builder.append(lines[i]).append(i == lines.length - 1 ? "" : (html ? "<br>" : "\n"));
}

return builder.append(html ? "</html>" : "").toString();
}

private static String[] collectSampleRenames(int truncationThreshold, Map<String, ClassRename> renameStack) {
int max = Math.min(renameStack.size(), truncationThreshold);
int index = 0;

String[] builder = new String[max];
for (Map.Entry<String, ClassRename> entry : renameStack.entrySet()) {
builder[index] = entry.getKey() + " -> " + entry.getValue().getNewName();
index++;

if (index >= max) {
return builder;
}
}

throw new RuntimeException("failed to collect sample renames!");
}

private void handleNode(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map<String, ClassRename> renameStack, TreeNode node) {
if (node instanceof ClassSelectorClassNode classNode && rename) {
String oldName = classNode.getDeobfEntry().getFullName();
int finalPackageIndex = divergenceIndex - 1;
int finalPackageIndex = divergenceIndex == 0 ? 0 : divergenceIndex - 1;

// skips all classes that do not match the exact package being renamed
if (this.mode == Mode.MOVE) {
Expand All @@ -190,59 +251,72 @@ private void handleNode(int divergenceIndex, boolean rename, String[] oldPackage
}
}

renameStack.put(oldName, () -> {
String[] split = oldName.split("/");
StringBuilder newPackages = new StringBuilder();

if (oldPackageNames.length <= newPackageNames.length) {
for (int i = finalPackageIndex; i < newPackageNames.length; i++) {
if (i >= 0) {
if (i < oldPackageNames.length && i < split.length && oldPackageNames[i].equals(split[i])) {
split[i] = newPackageNames[i];
} else {
newPackages.append("/").append(newPackageNames[i]);
renameStack.put(oldName, new ClassRename() {
private String cachedNewName = null;

@Override
public void executeRename() {
String newName = this.cachedNewName == null ? this.getNewName() : this.cachedNewName;

// ignore warnings, we don't want to bother the user with every individual package created
PackageRenamer.this.gui.getController().applyChange(new ValidationContext(PackageRenamer.this.gui.getNotificationManager(), false), EntryChange.modify(classNode.getObfEntry()).withDeobfName(newName), false);
}

@Override
public String getNewName() {
String[] split = oldName.split("/");
StringBuilder newPackages = new StringBuilder();

if (oldPackageNames.length <= newPackageNames.length) {
for (int i = finalPackageIndex; i < newPackageNames.length; i++) {
if (i >= 0) {
if (i < oldPackageNames.length && i < split.length && oldPackageNames[i].equals(split[i])) {
split[i] = newPackageNames[i];
} else {
newPackages.append("/").append(newPackageNames[i]);
}
}
}
}
} else {
for (int i = 0; i < oldPackageNames.length; i++) {
if (i > newPackageNames.length - 1 || !oldPackageNames[i].equals(newPackageNames[i])) {
StringBuilder string = new StringBuilder();
} else {
for (int i = 0; i < oldPackageNames.length; i++) {
if (i > newPackageNames.length - 1 || !oldPackageNames[i].equals(newPackageNames[i])) {
StringBuilder string = new StringBuilder();

// append preceding old package names
for (int j = 0; j <= i - 1; j++) {
appendSlash(string);
string.append(oldPackageNames[j]);
}
// append preceding old package names
for (int j = 0; j <= i - 1; j++) {
appendSlash(string);
string.append(oldPackageNames[j]);
}

// append new package names
for (int j = i; j < newPackageNames.length; j++) {
appendSlash(string);
string.append(newPackageNames[j]);
}
// append new package names
for (int j = i; j < newPackageNames.length; j++) {
appendSlash(string);
string.append(newPackageNames[j]);
}

// append the remaining old package names
for (int j = i - 1 + oldPackageNames.length; j < split.length - 1; j++) {
appendSlash(string);
string.append(split[j]);
}

// append the remaining old package names
for (int j = i - 1 + oldPackageNames.length; j < split.length - 1; j++) {
appendSlash(string);
string.append(split[j]);
string.append(classNode.getDeobfEntry().getSimpleName());
split = string.toString().split("/");
break;
}

appendSlash(string);
string.append(classNode.getDeobfEntry().getSimpleName());
split = string.toString().split("/");
break;
}
}
}

// append new packages to last package
if (!newPackages.toString().isBlank()) {
split[finalPackageIndex] = split[finalPackageIndex] + newPackages;
}
// append new packages to last package
if (!newPackages.toString().isBlank()) {
split[finalPackageIndex] = split[finalPackageIndex] + newPackages;
}

String newName = String.join("/", split);
// ignore warnings, we don't want to bother the user with every individual package created
this.gui.getController().applyChange(new ValidationContext(this.gui.getNotificationManager(), false), EntryChange.modify(classNode.getObfEntry()).withDeobfName(newName), false);
String newName = String.join("/", split);
this.cachedNewName = newName;
return newName;
}
});
} else if (node instanceof ClassSelectorPackageNode packageNode) {
String packageName = packageNode.getPackageName().substring(packageNode.getPackageName().lastIndexOf("/") + 1);
Expand All @@ -268,9 +342,9 @@ private void handleNode(int divergenceIndex, boolean rename, String[] oldPackage
}
}

if (packageName.equals(newPackageNames[index])) {
if (newPackageNames.length - 1 >= index && packageName.equals(newPackageNames[index])) {
this.handlePackage(index, false, oldPackageNames, newPackageNames, renameStack, packageNode);
} else if (packageName.equals(oldPackageNames[index])) {
} else if (oldPackageNames.length - 1 >= index && packageName.equals(oldPackageNames[index])) {
this.handlePackage(index, true, oldPackageNames, newPackageNames, renameStack, packageNode);
}
}
Expand All @@ -282,7 +356,7 @@ private static void appendSlash(StringBuilder string) {
}
}

private void handlePackage(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map<String, Runnable> renameStack, TreeNode node) {
private void handlePackage(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map<String, ClassRename> renameStack, TreeNode node) {
if (!rename) {
divergenceIndex++;
}
Expand All @@ -291,4 +365,10 @@ private void handlePackage(int divergenceIndex, boolean rename, String[] oldPack
this.handleNode(divergenceIndex, rename, oldPackageNames, newPackageNames, renameStack, node.getChildAt(j));
}
}

private interface ClassRename {
void executeRename();

String getNewName();
}
}
Loading

0 comments on commit a7f5833

Please sign in to comment.