Skip to content

Commit

Permalink
fix(gui): allow to rename packages (#987)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Oct 1, 2020
1 parent b76c882 commit c0a8197
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 47 deletions.
10 changes: 8 additions & 2 deletions jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -39,7 +39,13 @@ public JPackage(String name, JadxWrapper wrapper) {
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) {
Expand Down
1 change: 0 additions & 1 deletion jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ public class JRoot extends JNode {

public JRoot(JadxWrapper wrapper) {
this.wrapper = wrapper;
update();
}

public final void update() {
Expand Down
115 changes: 115 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/ui/JPackagePopupMenu.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
24 changes: 3 additions & 21 deletions jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
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;
Expand Down Expand Up @@ -503,10 +502,11 @@ private void saveAll(boolean export) {
}
}

private void initTree() {
public void initTree() {
treeRoot = new JRoot(wrapper);
treeRoot.setFlatPackages(isFlattenPackage);
treeModel.setRoot(treeRoot);
treeRoot.update();
reloadTree();
}

Expand Down Expand Up @@ -606,7 +606,7 @@ private void nodeClickAction(@Nullable Object obj) {
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());
}
}
Expand Down Expand Up @@ -1170,22 +1170,4 @@ public void menuDeselected(MenuEvent e) {
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();
});
}
}
}
74 changes: 52 additions & 22 deletions jadx-gui/src/main/java/jadx/gui/ui/RenameDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
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;

Expand All @@ -27,6 +29,7 @@
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;
Expand All @@ -35,7 +38,6 @@
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;
Expand All @@ -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()) {
Expand Down Expand Up @@ -117,17 +119,12 @@ private String getNodeAlias(String renameText) {
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);
}
Expand Down Expand Up @@ -158,18 +155,17 @@ private List<String> readDeobfMap(Path deobfMapPath) throws IOException {
}

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;
}
Expand Down Expand Up @@ -222,19 +218,53 @@ private void refreshState(RootNode rootNode) {
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());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void actionPerformed(ActionEvent e) {
LOG.info("node == null!");
return;
}
RenameDialog renameDialog = new RenameDialog(codeArea, node);
RenameDialog renameDialog = new RenameDialog(codeArea.getMainWindow(), node);
renameDialog.setVisible(true);
}

Expand Down

0 comments on commit c0a8197

Please sign in to comment.