From 925377b0998ae5c08d48e97cb0f7b15b809b061f Mon Sep 17 00:00:00 2001 From: ix0rai Date: Fri, 29 Sep 2023 20:19:26 -0500 Subject: [PATCH] add "move package" option to context menu (#151) * add "move package" context option * fix style --- .../gui/elements/ClassSelectorPopupMenu.java | 177 +---------- .../enigma/gui/util/PackageRenamer.java | 294 ++++++++++++++++++ .../java/cuchaz/enigma/TestPackageRename.java | 28 +- .../enigma/analysis/index/EntryIndex.java | 29 ++ enigma/src/main/resources/lang/en_us.json | 1 + enigma/src/main/resources/lang/fr_fr.json | 1 + 6 files changed, 356 insertions(+), 174 deletions(-) create mode 100644 enigma-swing/src/main/java/cuchaz/enigma/gui/util/PackageRenamer.java diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ClassSelectorPopupMenu.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ClassSelectorPopupMenu.java index 75137aced..d0ad705e5 100644 --- a/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ClassSelectorPopupMenu.java +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/elements/ClassSelectorPopupMenu.java @@ -2,26 +2,17 @@ import cuchaz.enigma.gui.ClassSelector; import cuchaz.enigma.gui.Gui; -import cuchaz.enigma.gui.dialog.ProgressDialog; import cuchaz.enigma.gui.docker.ClassesDocker; -import cuchaz.enigma.gui.docker.Docker; -import cuchaz.enigma.gui.node.ClassSelectorClassNode; -import cuchaz.enigma.gui.node.ClassSelectorPackageNode; +import cuchaz.enigma.gui.util.PackageRenamer; import cuchaz.enigma.translation.mapping.EntryChange; import cuchaz.enigma.translation.representation.entry.ClassEntry; import cuchaz.enigma.utils.I18n; -import cuchaz.enigma.utils.validation.Message; import cuchaz.enigma.utils.validation.ValidationContext; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; -import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; public class ClassSelectorPopupMenu { private final Gui gui; @@ -29,6 +20,7 @@ public class ClassSelectorPopupMenu { private final JPopupMenu ui; private final JMenuItem renamePackage = new JMenuItem(); + private final JMenuItem movePackage = new JMenuItem(); private final JMenuItem renameClass = new JMenuItem(); private final JMenuItem toggleMapping = new JMenuItem(); private final JMenuItem expandAll = new JMenuItem(); @@ -40,13 +32,15 @@ public ClassSelectorPopupMenu(Gui gui, ClassesDocker docker) { this.ui = new JPopupMenu(); this.ui.add(this.renamePackage); + this.ui.add(this.movePackage); this.ui.add(this.renameClass); this.ui.add(this.toggleMapping); this.ui.addSeparator(); this.ui.add(this.expandAll); this.ui.add(this.collapseAll); - this.renamePackage.addActionListener(a -> this.onRenamePackage()); + this.renamePackage.addActionListener(a -> this.onRenamePackage(PackageRenamer.Mode.REFACTOR)); + this.movePackage.addActionListener(a -> this.onRenamePackage(PackageRenamer.Mode.MOVE)); this.renameClass.addActionListener(a -> { String input = JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("popup_menu.class_selector.rename_class"), this.selector.getSelectedClassDeobf().getFullName()); @@ -68,7 +62,7 @@ public ClassSelectorPopupMenu(Gui gui, ClassesDocker docker) { this.retranslateUi(); } - private void onRenamePackage() { + private void onRenamePackage(PackageRenamer.Mode mode) { TreePath path; if (this.selector.getSelectedClassDeobf() != null @@ -102,163 +96,13 @@ private void onRenamePackage() { } String input = JOptionPane.showInputDialog(this.gui.getFrame(), I18n.translate("popup_menu.class_selector.package_rename.title"), pathString.toString()); - this.renamePackage(pathString.toString(), input); - } - - public CompletableFuture renamePackage(String path, String input) { - // validate input - if (input == null || !input.matches("[a-z0-9_/]+") || input.isBlank() || input.startsWith("/") || input.endsWith("/")) { - this.gui.getNotificationManager().notify(Message.INVALID_PACKAGE_NAME); - return CompletableFuture.supplyAsync(() -> null); - } - - // skip if identical - if (path.equals(input)) { - return CompletableFuture.supplyAsync(() -> null); + if (input != null) { + this.createPackageRenamer(mode).renamePackage(pathString.toString(), input); } - - String[] oldPackageNames = path.split("/"); - String[] newPackageNames = input.split("/"); - - Map renameStack = new HashMap<>(); - - return ProgressDialog.runOffThread(this.gui, listener -> { - listener.init(1, I18n.translate("popup_menu.class_selector.package_rename.discovering")); - TreeNode root = this.selector.getPackageManager().getRoot(); - - for (int i = 0; i < root.getChildCount(); i++) { - TreeNode node = root.getChildAt(i); - this.handleNode(0, false, oldPackageNames, newPackageNames, renameStack, node); - } - - listener.init(renameStack.size(), I18n.translate("popup_menu.class_selector.package_rename.renaming_classes")); - - Map> expansionStates = new HashMap<>(); - for (Docker docker : this.gui.getDockerManager().getDockers()) { - if (docker instanceof ClassesDocker classesDocker) { - expansionStates.put(classesDocker, classesDocker.getClassSelector().getExpansionState()); - } - } - - int i = 0; - for (var entry : renameStack.entrySet()) { - listener.step(i, I18n.translateFormatted("popup_menu.class_selector.package_rename.renaming_class", entry.getKey())); - entry.getValue().run(); - i++; - } - - for (var entry : expansionStates.entrySet()) { - ClassSelector classSelector = entry.getKey().getClassSelector(); - classSelector.reload(); - classSelector.restoreExpansionState(entry.getValue()); - } - }); } - private void handleNode(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map renameStack, TreeNode node) { - if (node instanceof ClassSelectorClassNode classNode && rename) { - String oldName = classNode.getDeobfEntry().getFullName(); - int finalPackageIndex = divergenceIndex - 1; - - 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]); - } - } - } - } 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 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]); - } - - 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; - } - - String newName = String.join("/", split); - this.gui.getController().applyChange(new ValidationContext(this.gui.getNotificationManager()), EntryChange.modify(classNode.getObfEntry()).withDeobfName(newName), false); - }); - } else if (node instanceof ClassSelectorPackageNode packageNode) { - String packageName = packageNode.getPackageName().substring(packageNode.getPackageName().lastIndexOf("/") + 1); - int index = packageNode.getPackageName().split("/").length - 1; - - if (rename) { - this.handlePackage(divergenceIndex, true, oldPackageNames, newPackageNames, renameStack, node); - return; - } - - // handle backwards renaming - if (oldPackageNames.length > newPackageNames.length) { - // if we are past the final index in the new packages, begin rename from previous - if (newPackageNames.length <= index) { - this.handlePackage(index - 1, true, oldPackageNames, newPackageNames, renameStack, node.getParent()); - return; - } - } else { - // handle appending new packages - if (newPackageNames.length > oldPackageNames.length && index == oldPackageNames.length - 1) { - this.handlePackage(index + 1, true, oldPackageNames, newPackageNames, renameStack, packageNode); - return; - } - } - - if (packageName.equals(newPackageNames[index])) { - this.handlePackage(index, false, oldPackageNames, newPackageNames, renameStack, packageNode); - } else if (packageName.equals(oldPackageNames[index])) { - this.handlePackage(index, true, oldPackageNames, newPackageNames, renameStack, packageNode); - } - } - } - - private static void appendSlash(StringBuilder string) { - if (!string.isEmpty()) { - string.append("/"); - } - } - - private void handlePackage(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map renameStack, TreeNode node) { - if (!rename) { - divergenceIndex++; - } - - for (int j = 0; j < node.getChildCount(); j++) { - this.handleNode(divergenceIndex, rename, oldPackageNames, newPackageNames, renameStack, node.getChildAt(j)); - } + public PackageRenamer createPackageRenamer(PackageRenamer.Mode mode) { + return new PackageRenamer(this.gui, this.selector, mode); } public void show(ClassSelector selector, int x, int y) { @@ -285,6 +129,7 @@ public void show(ClassSelector selector, int x, int y) { public void retranslateUi() { this.renamePackage.setText(I18n.translate("popup_menu.class_selector.rename_package")); + this.movePackage.setText(I18n.translate("popup_menu.class_selector.move_package")); this.renameClass.setText(I18n.translate("popup_menu.class_selector.rename_class")); this.expandAll.setText(I18n.translate("popup_menu.class_selector.expand_all")); this.collapseAll.setText(I18n.translate("popup_menu.class_selector.collapse_all")); diff --git a/enigma-swing/src/main/java/cuchaz/enigma/gui/util/PackageRenamer.java b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/PackageRenamer.java new file mode 100644 index 000000000..9f85deff4 --- /dev/null +++ b/enigma-swing/src/main/java/cuchaz/enigma/gui/util/PackageRenamer.java @@ -0,0 +1,294 @@ +package cuchaz.enigma.gui.util; + +import cuchaz.enigma.gui.ClassSelector; +import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.gui.dialog.ProgressDialog; +import cuchaz.enigma.gui.docker.ClassesDocker; +import cuchaz.enigma.gui.docker.Docker; +import cuchaz.enigma.gui.node.ClassSelectorClassNode; +import cuchaz.enigma.gui.node.ClassSelectorPackageNode; +import cuchaz.enigma.translation.mapping.EntryChange; +import cuchaz.enigma.utils.I18n; +import cuchaz.enigma.utils.validation.Message; +import cuchaz.enigma.utils.validation.ValidationContext; + +import javax.swing.tree.TreeNode; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class PackageRenamer { + private final Gui gui; + private final ClassSelector selector; + private final Mode mode; + + /** + * Creates a new package renamer that will use the given mode. + */ + public PackageRenamer(Gui gui, ClassSelector selector, Mode mode) { + this.gui = gui; + this.selector = selector; + this.mode = mode; + } + + /** + * Represents a different way that packages will be refactored. + */ + public enum Mode { + /** + * Renames packages according to your input. + * Takes the point where the package diverge, and refactors all classes in packages past that point. + * For example, when renaming the package {@code a/b/c} to {@code a/c}, Enigma will consider that you want to rename {@code b/c} to simply {@code c}, + * meaning that classes like {@code a/b/C} would end up merged into the package {@code a}: {@code a/C}. + * Examples: + *
    + *
  • + * Input: {@code a/b/c} -> {@code a/b/d} + *
    Result: + *
      + *
    • {@code A} -> no change
    • + *
    • {@code a/B} -> no change
    • + *
    • {@code a/b/C} -> no change
    • + *
    • {@code a/b/c/D} -> {@code a/b/d/D}
    • + *
    • {@code a/b/c/d/E} -> {@code a/b/d/d/E}
    • + *
    + *
  • + *
  • + * Input: {@code a/b/c} -> {@code a/c} + *
    Result: + *
      + *
    • {@code A} -> no change
    • + *
    • {@code a/B} -> no change
    • + *
    • {@code a/b/C} -> {@code a/C}
    • + *
    • {@code a/b/c/D} -> {@code a/c/D}
    • + *
    • {@code a/b/c/d/E} -> {@code a/c/d/E}
    • + *
    + *
  • + *
  • + * Input: {@code a/b/c} -> {@code a} + *
    Result: + *
      + *
    • {@code A} -> no change
    • + *
    • {@code a/B} -> no change
    • + *
    • {@code a/b/C} -> no change
    • + *
    • {@code a/b/c/D} -> {@code a/D}
    • + *
    • {@code a/b/c/d/E} -> {@code a/d/E}
    • + *
    + *
  • + *
+ */ + REFACTOR, + /** + * Moves the deepest package in the hierarchy according to your input. + * Examples: + *
    + *
  • + * Input: {@code a/b/c} -> {@code a/b/d} + *
    Result: + *
      + *
    • {@code A} -> no change
    • + *
    • {@code a/B} -> no change
    • + *
    • {@code a/b/C} -> no change
    • + *
    • {@code a/b/c/D} -> {@code a/b/d/D}
    • + *
    • {@code a/b/c/d/E} -> {@code a/b/d/d/E}
    • + *
    + *
  • + *
  • + * Input: {@code a/b/c} -> {@code a/c} + *
    Result: + *
      + *
    • {@code A} -> no change
    • + *
    • {@code a/B} -> no change
    • + *
    • {@code a/b/C} -> no change
    • + *
    • {@code a/b/c/D} -> {@code a/c/D}
    • + *
    • {@code a/b/c/d/E} -> {@code a/c/d/E}
    • + *
    + *
  • + *
  • + * Input: {@code a/b/c} -> {@code a} + *
    Result: + *
      + *
    • {@code A} -> no change
    • + *
    • {@code a/B} -> no change
    • + *
    • {@code a/b/C} -> no change
    • + *
    • {@code a/b/c/D} -> {@code a/c/D}
    • + *
    • {@code a/b/c/d/E} -> {@code a/c/d/E}
    • + *
    + *
  • + *
+ */ + MOVE + } + + /** + * Renames all classes inside the provided package recursively to match the new name. + * Result will differ according to the given {@link Mode}, refer to the mode's documentation for more info. + * @param path the original package + * @param input the new name for the package + * @return a future that will complete when all classes have been renamed + */ + public CompletableFuture renamePackage(String path, String input) { + // validate input + if (input == null || !input.matches("[a-z0-9_/]+") || input.isBlank() || input.startsWith("/") || input.endsWith("/")) { + this.gui.getNotificationManager().notify(Message.INVALID_PACKAGE_NAME); + return CompletableFuture.supplyAsync(() -> null); + } + + // skip if identical + if (path.equals(input)) { + return CompletableFuture.supplyAsync(() -> null); + } + + String[] oldPackageNames = path.split("/"); + String[] newPackageNames = input.split("/"); + + Map renameStack = new HashMap<>(); + + return ProgressDialog.runOffThread(this.gui, listener -> { + listener.init(1, I18n.translate("popup_menu.class_selector.package_rename.discovering")); + TreeNode root = this.selector.getPackageManager().getRoot(); + + for (int i = 0; i < root.getChildCount(); i++) { + TreeNode node = root.getChildAt(i); + this.handleNode(0, false, oldPackageNames, newPackageNames, renameStack, node); + } + + listener.init(renameStack.size(), I18n.translate("popup_menu.class_selector.package_rename.renaming_classes")); + + Map> expansionStates = new HashMap<>(); + for (Docker docker : this.gui.getDockerManager().getDockers()) { + if (docker instanceof ClassesDocker classesDocker) { + expansionStates.put(classesDocker, classesDocker.getClassSelector().getExpansionState()); + } + } + + int i = 0; + for (var entry : renameStack.entrySet()) { + listener.step(i, I18n.translateFormatted("popup_menu.class_selector.package_rename.renaming_class", entry.getKey())); + entry.getValue().run(); + i++; + } + + for (var entry : expansionStates.entrySet()) { + ClassSelector classSelector = entry.getKey().getClassSelector(); + classSelector.reload(); + classSelector.restoreExpansionState(entry.getValue()); + } + }); + } + + private void handleNode(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map renameStack, TreeNode node) { + if (node instanceof ClassSelectorClassNode classNode && rename) { + String oldName = classNode.getDeobfEntry().getFullName(); + int finalPackageIndex = divergenceIndex - 1; + + // skips all classes that do not match the exact package being renamed + if (this.mode == Mode.MOVE) { + if (!oldName.equals(String.join("/", oldPackageNames) + "/" + classNode.getDeobfEntry().getSimpleName())) { + System.out.println("ignoring: " + oldName); + return; + } + } + + 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]); + } + } + } + } 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 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]); + } + + 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; + } + + String newName = String.join("/", split); + this.gui.getController().applyChange(new ValidationContext(this.gui.getNotificationManager()), EntryChange.modify(classNode.getObfEntry()).withDeobfName(newName), false); + }); + } else if (node instanceof ClassSelectorPackageNode packageNode) { + String packageName = packageNode.getPackageName().substring(packageNode.getPackageName().lastIndexOf("/") + 1); + int index = packageNode.getPackageName().split("/").length - 1; + + if (rename) { + this.handlePackage(divergenceIndex, true, oldPackageNames, newPackageNames, renameStack, node); + return; + } + + // handle backwards renaming + if (oldPackageNames.length > newPackageNames.length) { + // if we are past the final index in the new packages, begin rename from previous + if (newPackageNames.length <= index) { + this.handlePackage(index - 1, true, oldPackageNames, newPackageNames, renameStack, node.getParent()); + return; + } + } else { + // handle appending new packages + if (newPackageNames.length > oldPackageNames.length && index == oldPackageNames.length - 1) { + this.handlePackage(index + 1, true, oldPackageNames, newPackageNames, renameStack, packageNode); + return; + } + } + + if (packageName.equals(newPackageNames[index])) { + this.handlePackage(index, false, oldPackageNames, newPackageNames, renameStack, packageNode); + } else if (packageName.equals(oldPackageNames[index])) { + this.handlePackage(index, true, oldPackageNames, newPackageNames, renameStack, packageNode); + } + } + } + + private static void appendSlash(StringBuilder string) { + if (!string.isEmpty()) { + string.append("/"); + } + } + + private void handlePackage(int divergenceIndex, boolean rename, String[] oldPackageNames, String[] newPackageNames, Map renameStack, TreeNode node) { + if (!rename) { + divergenceIndex++; + } + + for (int j = 0; j < node.getChildCount(); j++) { + this.handleNode(divergenceIndex, rename, oldPackageNames, newPackageNames, renameStack, node.getChildAt(j)); + } + } +} diff --git a/enigma-swing/src/test/java/cuchaz/enigma/TestPackageRename.java b/enigma-swing/src/test/java/cuchaz/enigma/TestPackageRename.java index 6b175fcdc..070c68c08 100644 --- a/enigma-swing/src/test/java/cuchaz/enigma/TestPackageRename.java +++ b/enigma-swing/src/test/java/cuchaz/enigma/TestPackageRename.java @@ -4,6 +4,7 @@ import cuchaz.enigma.gui.Gui; import cuchaz.enigma.gui.docker.AllClassesDocker; import cuchaz.enigma.gui.elements.ClassSelectorPopupMenu; +import cuchaz.enigma.gui.util.PackageRenamer; import cuchaz.enigma.translation.TranslateResult; import cuchaz.enigma.translation.Translator; import cuchaz.enigma.translation.mapping.serde.MappingFormat; @@ -30,7 +31,7 @@ public class TestPackageRename { @Test void testRemoveOnePackage() throws InterruptedException { - renamePackage("a/b/c", "a/c"); + renamePackage("a/b/c", "a/c", PackageRenamer.Mode.REFACTOR); assertMapping(newClass("A"), newClass("a/c/A")); assertMapping(newClass("B"), newClass("a/c/B")); @@ -41,7 +42,7 @@ void testRemoveOnePackage() throws InterruptedException { @Test void testRemoveTwoPackages() throws InterruptedException { - renamePackage("a/b/c", "a"); + renamePackage("a/b/c", "a", PackageRenamer.Mode.REFACTOR); assertMapping(newClass("A"), newClass("a/A")); assertMapping(newClass("B"), newClass("a/B")); @@ -52,7 +53,7 @@ void testRemoveTwoPackages() throws InterruptedException { @Test void testPackageConservation() throws InterruptedException { - renamePackage("a/b", "a"); + renamePackage("a/b", "a", PackageRenamer.Mode.REFACTOR); assertMapping(newClass("A"), newClass("a/c/A")); assertMapping(newClass("B"), newClass("a/c/B")); @@ -63,7 +64,7 @@ void testPackageConservation() throws InterruptedException { @Test void testAppendOnePackage() throws InterruptedException { - renamePackage("a/b/c", "a/b/c/d"); + renamePackage("a/b/c", "a/b/c/d", PackageRenamer.Mode.REFACTOR); assertMapping(newClass("A"), newClass("a/b/c/d/A")); assertMapping(newClass("B"), newClass("a/b/c/d/B")); @@ -74,7 +75,7 @@ void testAppendOnePackage() throws InterruptedException { @Test void testSimpleRename() throws InterruptedException { - renamePackage("a/b/c", "a/b/d"); + renamePackage("a/b/c", "a/b/d", PackageRenamer.Mode.REFACTOR); assertMapping(newClass("A"), newClass("a/b/d/A")); assertMapping(newClass("B"), newClass("a/b/d/B")); @@ -85,7 +86,7 @@ void testSimpleRename() throws InterruptedException { @Test void testFirstPackageRename() throws InterruptedException { - renamePackage("a", "b"); + renamePackage("a", "b", PackageRenamer.Mode.REFACTOR); assertMapping(newClass("A"), newClass("b/b/c/A")); assertMapping(newClass("B"), newClass("b/b/c/B")); @@ -94,12 +95,23 @@ void testFirstPackageRename() throws InterruptedException { assertMapping(newClass("E"), newClass("E")); } - private static void renamePackage(String packageName, String newName) throws InterruptedException { + @Test + void testPackageMove() throws InterruptedException { + renamePackage("a/b/c", "a/c", PackageRenamer.Mode.MOVE); + + assertMapping(newClass("A"), newClass("a/c/A")); + assertMapping(newClass("B"), newClass("a/c/B")); + assertMapping(newClass("C"), newClass("a/b/C")); + assertMapping(newClass("D"), newClass("a/D")); + assertMapping(newClass("E"), newClass("E")); + } + + private static void renamePackage(String packageName, String newName, PackageRenamer.Mode mode) throws InterruptedException { ClassSelectorPopupMenu menu = setupMenu(); assertBaseMappings(); CountDownLatch packageRenameLatch = new CountDownLatch(1); - menu.renamePackage(packageName, newName).thenRun(packageRenameLatch::countDown); + menu.createPackageRenamer(mode).renamePackage(packageName, newName).thenRun(packageRenameLatch::countDown); packageRenameLatch.await(); } diff --git a/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java b/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java index 4b9aaf2e2..5046bd4ac 100644 --- a/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java +++ b/enigma/src/main/java/cuchaz/enigma/analysis/index/EntryIndex.java @@ -1,5 +1,8 @@ package cuchaz.enigma.analysis.index; +import cuchaz.enigma.translation.mapping.EntryMapping; +import cuchaz.enigma.translation.mapping.tree.EntryTree; +import cuchaz.enigma.translation.mapping.tree.HashEntryTree; import cuchaz.enigma.translation.representation.AccessFlags; import cuchaz.enigma.translation.representation.entry.ClassDefEntry; import cuchaz.enigma.translation.representation.entry.ClassEntry; @@ -16,6 +19,8 @@ import java.util.Map; public class EntryIndex implements JarIndexer { + private final EntryTree tree = new HashEntryTree<>(); + private final Map classes = new HashMap<>(); private final Map fields = new HashMap<>(); private final Map methods = new HashMap<>(); @@ -37,6 +42,21 @@ public void indexField(FieldDefEntry fieldEntry) { this.fields.put(fieldEntry, fieldEntry.getAccess()); } + @Override + public void processIndex(JarIndex index) { + for (ClassEntry entry : this.getClasses()) { + this.tree.insert(entry, null); + } + + for (FieldEntry entry : this.getFields()) { + this.tree.insert(entry, null); + } + + for (MethodEntry entry : this.getMethods()) { + this.tree.insert(entry, null); + } + } + public boolean hasClass(ClassEntry entry) { return this.classes.containsKey(entry); } @@ -109,6 +129,15 @@ public Collection getFields() { return this.fields.keySet(); } + /** + * Returns all indexed entries, organised into an {@link EntryTree}. + * Note that all entries will have their mapping set to {@code null}. + * @return the entry tree + */ + public EntryTree getTree() { + return this.tree; + } + @Override public String getTranslationKey() { return "progress.jar.indexing.process.entries"; diff --git a/enigma/src/main/resources/lang/en_us.json b/enigma/src/main/resources/lang/en_us.json index 441557b34..1b6f91a0e 100644 --- a/enigma/src/main/resources/lang/en_us.json +++ b/enigma/src/main/resources/lang/en_us.json @@ -115,6 +115,7 @@ "popup_menu.editor_tab.close_left": "Close All to the Left", "popup_menu.editor_tab.close_right": "Close All to the Right", "popup_menu.class_selector.rename_package": "Rename Package", + "popup_menu.class_selector.move_package": "Move Package", "popup_menu.class_selector.rename_class": "Rename Class", "popup_menu.class_selector.expand_all": "Expand All", "popup_menu.class_selector.collapse_all": "Collapse All", diff --git a/enigma/src/main/resources/lang/fr_fr.json b/enigma/src/main/resources/lang/fr_fr.json index 133f468e0..c404a4f22 100644 --- a/enigma/src/main/resources/lang/fr_fr.json +++ b/enigma/src/main/resources/lang/fr_fr.json @@ -111,6 +111,7 @@ "popup_menu.editor_tab.close_left": "Tout fermer sur la gauche", "popup_menu.editor_tab.close_right": "Tout fermer sur la droite", "popup_menu.class_selector.rename_package": "Renommer le package", + "popup_menu.class_selector.move_package": "Bouger le package", "popup_menu.class_selector.rename_class": "Renommer la classe", "popup_menu.class_selector.expand_all": "Tout développer", "popup_menu.class_selector.collapse_all": "Tout réduire",