提交 c0a81978 编写于 作者: S Skylot

fix(gui): allow to rename packages (#987)

上级 b76c8822
......@@ -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() {
......
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);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册