fix(gui): allow to rename packages (#987)
This commit is contained in:
@@ -21,7 +21,7 @@ public class JPackage extends JNode implements Comparable<JPackage> {
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private final List<JClass> classes;
|
||||
private final List<JPackage> innerPackages = new ArrayList<>(1);
|
||||
private final List<JPackage> innerPackages = new ArrayList<>();
|
||||
|
||||
public JPackage(JavaPackage pkg, JadxWrapper wrapper) {
|
||||
this.fullName = pkg.getName();
|
||||
@@ -39,7 +39,13 @@ public class JPackage extends JNode implements Comparable<JPackage> {
|
||||
this.fullName = name;
|
||||
this.name = name;
|
||||
setEnabled(wrapper);
|
||||
this.classes = new ArrayList<>(1);
|
||||
this.classes = new ArrayList<>();
|
||||
}
|
||||
|
||||
public JPackage(String fullName, String name) {
|
||||
this.fullName = fullName;
|
||||
this.name = name;
|
||||
this.classes = new ArrayList<>();
|
||||
}
|
||||
|
||||
private void setEnabled(JadxWrapper wrapper) {
|
||||
|
||||
@@ -25,7 +25,6 @@ public class JRoot extends JNode {
|
||||
|
||||
public JRoot(JadxWrapper wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
update();
|
||||
}
|
||||
|
||||
public final void update() {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package jadx.gui.ui;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JPackage;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
class JPackagePopupMenu extends JPopupMenu {
|
||||
private static final long serialVersionUID = -7781009781149224131L;
|
||||
|
||||
private final transient MainWindow mainWindow;
|
||||
|
||||
public JPackagePopupMenu(MainWindow mainWindow, JPackage pkg) {
|
||||
this.mainWindow = mainWindow;
|
||||
|
||||
add(makeExcludeItem(pkg));
|
||||
JMenuItem menuItem = makeRenameMenuItem(pkg);
|
||||
if (menuItem != null) {
|
||||
add(menuItem);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JMenuItem makeRenameMenuItem(JPackage pkg) {
|
||||
List<String> aliasParts = splitPackage(pkg.getName());
|
||||
int count = aliasParts.size();
|
||||
if (count == 0) {
|
||||
return null;
|
||||
}
|
||||
String rawPackage = getRawPackage(pkg);
|
||||
if (rawPackage == null) {
|
||||
return null;
|
||||
}
|
||||
if (count == 1) {
|
||||
// single case => no submenu
|
||||
String aliasPkg = aliasParts.get(0);
|
||||
JPackage renamePkg = new JPackage(rawPackage, aliasPkg);
|
||||
JMenuItem pkgItem = new JMenuItem(NLS.str("popup.rename"));
|
||||
pkgItem.addActionListener(e -> rename(renamePkg));
|
||||
return pkgItem;
|
||||
}
|
||||
List<String> rawParts = splitPackage(rawPackage); // can be longer then alias
|
||||
JMenuItem renameSubMenu = new JMenu(NLS.str("popup.rename"));
|
||||
for (int i = 0; i < count; i++) {
|
||||
String rawPkg = concat(rawParts, i);
|
||||
String aliasShortPkg = aliasParts.get(i);
|
||||
JPackage pkgPart = new JPackage(rawPkg, aliasShortPkg);
|
||||
JMenuItem pkgPartItem = new JMenuItem(aliasShortPkg);
|
||||
pkgPartItem.addActionListener(e -> rename(pkgPart));
|
||||
renameSubMenu.add(pkgPartItem);
|
||||
}
|
||||
return renameSubMenu;
|
||||
}
|
||||
|
||||
private String concat(List<String> parts, int n) {
|
||||
if (n == 0) {
|
||||
return parts.get(0);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(parts.get(0));
|
||||
int count = parts.size();
|
||||
for (int i = 1; i < count && i <= n; i++) {
|
||||
sb.append('.');
|
||||
sb.append(parts.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void rename(JPackage pkgPart) {
|
||||
new RenameDialog(mainWindow, pkgPart).setVisible(true);
|
||||
}
|
||||
|
||||
private List<String> splitPackage(String rawPackage) {
|
||||
return Arrays.asList(rawPackage.split("\\."));
|
||||
}
|
||||
|
||||
private String getRawPackage(JPackage pkg) {
|
||||
for (JClass cls : pkg.getClasses()) {
|
||||
return cls.getRootClass().getCls().getClassNode().getClassInfo().getPackage();
|
||||
}
|
||||
for (JPackage innerPkg : pkg.getInnerPackages()) {
|
||||
String rawPackage = getRawPackage(innerPkg);
|
||||
if (rawPackage != null) {
|
||||
return rawPackage;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private JMenuItem makeExcludeItem(JPackage pkg) {
|
||||
JMenuItem excludeItem = new JCheckBoxMenuItem(NLS.str("popup.exclude"));
|
||||
excludeItem.setSelected(!pkg.isEnabled());
|
||||
excludeItem.addItemListener(e -> {
|
||||
JadxWrapper wrapper = mainWindow.getWrapper();
|
||||
String fullName = pkg.getFullName();
|
||||
if (excludeItem.isSelected()) {
|
||||
wrapper.addExcludedPackage(fullName);
|
||||
} else {
|
||||
wrapper.removeExcludedPackage(fullName);
|
||||
}
|
||||
mainWindow.reOpenFile();
|
||||
});
|
||||
return excludeItem;
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,6 @@ import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JToggleButton;
|
||||
@@ -503,10 +502,11 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
private void initTree() {
|
||||
public void initTree() {
|
||||
treeRoot = new JRoot(wrapper);
|
||||
treeRoot.setFlatPackages(isFlattenPackage);
|
||||
treeModel.setRoot(treeRoot);
|
||||
treeRoot.update();
|
||||
reloadTree();
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ public class MainWindow extends JFrame {
|
||||
private void treeRightClickAction(MouseEvent e) {
|
||||
Object obj = getJNodeUnderMouse(e);
|
||||
if (obj instanceof JPackage) {
|
||||
JPackagePopUp menu = new JPackagePopUp((JPackage) obj);
|
||||
JPackagePopupMenu menu = new JPackagePopupMenu(this, (JPackage) obj);
|
||||
menu.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
@@ -1170,22 +1170,4 @@ public class MainWindow extends JFrame {
|
||||
public void menuCanceled(MenuEvent e) {
|
||||
}
|
||||
}
|
||||
|
||||
private class JPackagePopUp extends JPopupMenu {
|
||||
JMenuItem excludeItem = new JCheckBoxMenuItem(NLS.str("popup.exclude"));
|
||||
|
||||
public JPackagePopUp(JPackage pkg) {
|
||||
excludeItem.setSelected(!pkg.isEnabled());
|
||||
add(excludeItem);
|
||||
excludeItem.addItemListener(e -> {
|
||||
String fullName = pkg.getFullName();
|
||||
if (excludeItem.isSelected()) {
|
||||
wrapper.addExcludedPackage(fullName);
|
||||
} else {
|
||||
wrapper.removeExcludedPackage(fullName);
|
||||
}
|
||||
reOpenFile();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -27,6 +29,7 @@ import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.RenameVisitor;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.jobs.IndexJob;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
@@ -35,7 +38,6 @@ import jadx.gui.treemodel.JMethod;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JPackage;
|
||||
import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.ui.codearea.CodePanel;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
@@ -52,9 +54,9 @@ public class RenameDialog extends JDialog {
|
||||
private final transient JNode node;
|
||||
private transient JTextField renameField;
|
||||
|
||||
public RenameDialog(CodeArea codeArea, JNode node) {
|
||||
super(codeArea.getMainWindow());
|
||||
this.mainWindow = codeArea.getMainWindow();
|
||||
public RenameDialog(MainWindow mainWindow, JNode node) {
|
||||
super(mainWindow);
|
||||
this.mainWindow = mainWindow;
|
||||
this.cache = mainWindow.getCacheObject();
|
||||
this.node = node;
|
||||
if (checkSettings()) {
|
||||
@@ -117,17 +119,12 @@ public class RenameDialog extends JDialog {
|
||||
type = "f";
|
||||
id = javaField.getFieldNode().getFieldInfo().getRawFullId();
|
||||
} else if (node instanceof JClass) {
|
||||
JavaClass javaClass = (JavaClass) node.getJavaNode();
|
||||
type = "c";
|
||||
JavaNode javaNode = node.getJavaNode();
|
||||
id = javaNode.getFullName();
|
||||
if (javaNode instanceof JavaClass) {
|
||||
JavaClass javaClass = (JavaClass) javaNode;
|
||||
id = javaClass.getRawName();
|
||||
}
|
||||
|
||||
id = javaClass.getRawName();
|
||||
} else if (node instanceof JPackage) {
|
||||
type = "p";
|
||||
id = node.getJavaNode().getFullName();
|
||||
id = ((JPackage) node).getFullName();
|
||||
}
|
||||
return String.format("%s %s = %s", type, id, renameText);
|
||||
}
|
||||
@@ -158,18 +155,17 @@ public class RenameDialog extends JDialog {
|
||||
}
|
||||
|
||||
private List<String> updateDeobfMap(List<String> deobfMap, String alias) {
|
||||
LOG.trace("updateDeobfMap(): alias = " + alias);
|
||||
String id = alias.split("=")[0];
|
||||
String id = alias.substring(0, alias.indexOf('=') + 1);
|
||||
int i = 0;
|
||||
while (i < deobfMap.size()) {
|
||||
if (deobfMap.get(i).startsWith(id)) {
|
||||
LOG.info("updateDeobfMap(): Removing entry " + deobfMap.get(i));
|
||||
LOG.debug("updateDeobfMap(): Removing entry " + deobfMap.get(i));
|
||||
deobfMap.remove(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
LOG.trace("updateDeobfMap(): Placing alias = " + alias);
|
||||
LOG.debug("updateDeobfMap(): Placing alias = " + alias);
|
||||
deobfMap.add(alias);
|
||||
return deobfMap;
|
||||
}
|
||||
@@ -222,19 +218,53 @@ public class RenameDialog extends JDialog {
|
||||
renameVisitor.init(rootNode);
|
||||
|
||||
JNodeCache nodeCache = cache.getNodeCache();
|
||||
Set<JClass> updatedClasses = node.getJavaNode().getUseIn()
|
||||
JavaNode javaNode = node.getJavaNode();
|
||||
|
||||
List<JavaNode> toUpdate = new ArrayList<>();
|
||||
if (javaNode != null) {
|
||||
toUpdate.add(javaNode);
|
||||
toUpdate.addAll(javaNode.getUseIn());
|
||||
} else if (node instanceof JPackage) {
|
||||
processPackage(toUpdate);
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unexpected node type: " + node);
|
||||
}
|
||||
Set<JClass> updatedTopClasses = toUpdate
|
||||
.stream()
|
||||
.map(nodeCache::makeFrom)
|
||||
.map(JNode::getRootClass)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
updatedClasses.add(node.getRootClass());
|
||||
|
||||
refreshTabs(mainWindow.getTabbedPane(), updatedClasses);
|
||||
LOG.debug("Classes to update: {}", updatedTopClasses);
|
||||
|
||||
if (!updatedClasses.isEmpty()) {
|
||||
refreshTabs(mainWindow.getTabbedPane(), updatedTopClasses);
|
||||
|
||||
if (!updatedTopClasses.isEmpty()) {
|
||||
mainWindow.getBackgroundExecutor().execute("Refreshing",
|
||||
Utils.collectionMap(updatedClasses, cls -> () -> refreshJClass(cls)),
|
||||
mainWindow::reloadTree);
|
||||
Utils.collectionMap(updatedTopClasses, cls -> () -> refreshJClass(cls)),
|
||||
() -> {
|
||||
if (node instanceof JPackage) {
|
||||
// reinit tree
|
||||
mainWindow.initTree();
|
||||
} else {
|
||||
mainWindow.reloadTree();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void processPackage(List<JavaNode> toUpdate) {
|
||||
String rawFullPkg = ((JPackage) node).getFullName();
|
||||
String rawFullPkgDot = rawFullPkg + ".";
|
||||
for (JavaClass cls : mainWindow.getWrapper().getClasses()) {
|
||||
String clsPkg = cls.getClassNode().getClassInfo().getPackage();
|
||||
// search all classes in package
|
||||
if (clsPkg.equals(rawFullPkg) || clsPkg.startsWith(rawFullPkgDot)) {
|
||||
toUpdate.add(cls);
|
||||
// also include all usages (for import fix)
|
||||
toUpdate.addAll(cls.getUseIn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public final class RenameAction extends JNodeMenuAction<JNode> {
|
||||
LOG.info("node == null!");
|
||||
return;
|
||||
}
|
||||
RenameDialog renameDialog = new RenameDialog(codeArea, node);
|
||||
RenameDialog renameDialog = new RenameDialog(codeArea.getMainWindow(), node);
|
||||
renameDialog.setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user