gui: More advanced implementation of renaming
This implementation does not reload file after renaming, and so works faster.
This commit is contained in:
@@ -8,4 +8,6 @@ public interface ICodeCache {
|
||||
|
||||
@Nullable
|
||||
ICodeInfo get(String clsFullName);
|
||||
|
||||
void remove(String clsFullName);
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ public final class JavaClass implements JavaNode {
|
||||
cls.decompile();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
cls.refresh();
|
||||
}
|
||||
|
||||
public synchronized String getSmali() {
|
||||
return cls.getSmali();
|
||||
}
|
||||
|
||||
@@ -21,4 +21,9 @@ public class InMemoryCodeCache implements ICodeCache {
|
||||
public @Nullable ICodeInfo get(String clsFullName) {
|
||||
return storage.get(clsFullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String clsFullName) {
|
||||
storage.remove(clsFullName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,8 @@ public class NoOpCodeCache implements ICodeCache {
|
||||
public @Nullable ICodeInfo get(String clsFullName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(String clsFullName) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +289,11 @@ public class ClassNode extends LineAttrNode implements ILoadable, ICodeNode {
|
||||
return codeInfo;
|
||||
}
|
||||
|
||||
public synchronized ICodeInfo refresh() {
|
||||
load();
|
||||
return decompile(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (MethodNode mth : getMethods()) {
|
||||
|
||||
@@ -59,6 +59,13 @@ public class JClass extends JLoadableNode {
|
||||
update();
|
||||
}
|
||||
|
||||
public synchronized void refresh() {
|
||||
cls.refresh();
|
||||
loaded = true;
|
||||
update();
|
||||
cls.unload();
|
||||
}
|
||||
|
||||
public synchronized void update() {
|
||||
removeAllChildren();
|
||||
if (!loaded) {
|
||||
|
||||
@@ -396,6 +396,11 @@ public class MainWindow extends JFrame {
|
||||
cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount));
|
||||
}
|
||||
|
||||
void resetIndex() {
|
||||
int threadsCount = settings.getThreadsCount();
|
||||
cacheObject.setIndexJob(new IndexJob(wrapper, cacheObject, threadsCount));
|
||||
}
|
||||
|
||||
private synchronized void runBackgroundJobs() {
|
||||
cancelBackgroundJobs();
|
||||
backgroundWorker = new BackgroundWorker(cacheObject, progressPane);
|
||||
@@ -512,7 +517,7 @@ public class MainWindow extends JFrame {
|
||||
treeModel.reload();
|
||||
}
|
||||
|
||||
private void reloadTree() {
|
||||
void reloadTree() {
|
||||
treeReloading = true;
|
||||
|
||||
treeModel.reload();
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
@@ -23,13 +24,18 @@ import jadx.api.JavaMethod;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.RenameVisitor;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.gui.treemodel.CodeNode;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JField;
|
||||
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.CodeUsageInfo;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.TextStandardActions;
|
||||
|
||||
@@ -105,42 +111,37 @@ public class RenameDialog extends JDialog {
|
||||
return String.format("%s %s = %s", type, id, renameText);
|
||||
}
|
||||
|
||||
private boolean updateDeobfMap(String renameText, RootNode root) {
|
||||
Path deobfMapPath = getDeobfMapPath(root);
|
||||
private boolean writeDeobfMapFile(Path deobfMapPath, List<String> deobfMap) throws IOException {
|
||||
if (deobfMapPath == null) {
|
||||
LOG.error("rename(): Failed deofbMapFile is null");
|
||||
LOG.error("updateDeobfMapFile(): deobfMapPath is null!");
|
||||
return false;
|
||||
}
|
||||
String alias = getNodeAlias(renameText);
|
||||
LOG.info("rename(): {}", alias);
|
||||
try {
|
||||
List<String> deobfMap = readAndUpdateDeobfMap(deobfMapPath, alias);
|
||||
File tmpFile = File.createTempFile("deobf_tmp_", ".txt");
|
||||
try (FileOutputStream fileOut = new FileOutputStream(tmpFile)) {
|
||||
for (String entry : deobfMap) {
|
||||
fileOut.write(entry.getBytes());
|
||||
fileOut.write(System.lineSeparator().getBytes());
|
||||
}
|
||||
}
|
||||
File oldMap = File.createTempFile("deobf_bak_", ".txt");
|
||||
Files.copy(deobfMapPath, oldMap.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(tmpFile.toPath(), deobfMapPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.delete(oldMap.toPath());
|
||||
} catch (Exception e) {
|
||||
LOG.error("rename(): Failed to write deofbMapFile {}", deobfMapPath, e);
|
||||
return false;
|
||||
|
||||
File tmpFile = File.createTempFile("deobf_tmp_", ".txt");
|
||||
FileOutputStream fileOut = new FileOutputStream(tmpFile);
|
||||
for (String entry : deobfMap) {
|
||||
fileOut.write(entry.getBytes());
|
||||
fileOut.write(System.lineSeparator().getBytes());
|
||||
}
|
||||
fileOut.close();
|
||||
File oldMap = File.createTempFile("deobf_bak_", ".txt");
|
||||
Files.copy(deobfMapPath, oldMap.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(tmpFile.toPath(), deobfMapPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.delete(oldMap.toPath());
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<String> readAndUpdateDeobfMap(Path deobfMapPath, String alias) throws IOException {
|
||||
List<String> deobfMap = Files.readAllLines(deobfMapPath, StandardCharsets.UTF_8);
|
||||
@NotNull
|
||||
private List<String> readDeobfMap(Path deobfMapPath) throws IOException {
|
||||
return Files.readAllLines(deobfMapPath, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private List<String> updateDeobfMap(List<String> deobfMap, String alias) {
|
||||
String id = alias.split("=")[0];
|
||||
LOG.info("Id = {}", id);
|
||||
int i = 0;
|
||||
while (i < deobfMap.size()) {
|
||||
if (deobfMap.get(i).startsWith(id)) {
|
||||
LOG.info("Removing entry {}", deobfMap.get(i));
|
||||
LOG.info("updateDeobfMap(): Removing entry " + deobfMap.get(i));
|
||||
deobfMap.remove(i);
|
||||
} else {
|
||||
i++;
|
||||
@@ -161,15 +162,79 @@ public class RenameDialog extends JDialog {
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
if (!updateDeobfMap(renameText, root)) {
|
||||
LOG.error("rename(): updateDeobfMap() failed");
|
||||
if (!refreshDeobfMapFile(renameText, root)) {
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
mainWindow.reOpenFile();
|
||||
refreshState(root);
|
||||
dispose();
|
||||
}
|
||||
|
||||
private boolean refreshDeobfMapFile(String renameText, RootNode root) {
|
||||
List<String> deobfMap;
|
||||
Path deobfMapPath = getDeobfMapPath(root);
|
||||
try {
|
||||
deobfMap = readDeobfMap(deobfMapPath);
|
||||
} catch (IOException e) {
|
||||
LOG.error("rename(): readDeobfMap() failed");
|
||||
dispose();
|
||||
return false;
|
||||
}
|
||||
updateDeobfMap(deobfMap, getNodeAlias(renameText));
|
||||
try {
|
||||
writeDeobfMapFile(deobfMapPath, deobfMap);
|
||||
} catch (IOException e) {
|
||||
LOG.error("rename(): writeDeobfMap() failed");
|
||||
dispose();
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
writeDeobfMapFile(deobfMapPath, deobfMap);
|
||||
} catch (IOException e) {
|
||||
LOG.error("rename(): updateDeobfMap() failed");
|
||||
dispose();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void refreshState(RootNode rootNode) {
|
||||
RenameVisitor renameVisitor = new RenameVisitor();
|
||||
renameVisitor.init(rootNode);
|
||||
|
||||
mainWindow.getCacheObject().getNodeCache().refresh(node);
|
||||
|
||||
TabbedPane tabbedPane = mainWindow.getTabbedPane();
|
||||
refreshTabs(tabbedPane);
|
||||
refreshUsages();
|
||||
mainWindow.resetIndex();
|
||||
mainWindow.reloadTree();
|
||||
}
|
||||
|
||||
private void refreshTabs(TabbedPane tabbedPane) {
|
||||
for (Map.Entry<JNode, ContentPanel> panel : tabbedPane.getOpenTabs().entrySet()) {
|
||||
ContentPanel contentPanel = panel.getValue();
|
||||
if (contentPanel instanceof ClassCodeContentPanel) {
|
||||
JNode node = panel.getKey();
|
||||
node.getRootClass().refresh(); // Update code cache
|
||||
ClassCodeContentPanel codePanel = (ClassCodeContentPanel) contentPanel;
|
||||
CodePanel javaPanel = codePanel.getJavaCodePanel();
|
||||
javaPanel.refresh();
|
||||
tabbedPane.refresh(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshUsages() {
|
||||
CodeUsageInfo usageInfo = mainWindow.getCacheObject().getUsageInfo();
|
||||
if (usageInfo == null) {
|
||||
return;
|
||||
}
|
||||
for (CodeNode node : usageInfo.getUsageList(node)) {
|
||||
node.getRootClass().refresh(); // Update code cache
|
||||
}
|
||||
}
|
||||
|
||||
private void initCommon() {
|
||||
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||
getRootPane().registerKeyboardAction(e -> dispose(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
|
||||
@@ -155,6 +155,13 @@ public class TabbedPane extends JTabbedPane {
|
||||
return panel;
|
||||
}
|
||||
|
||||
public void refresh(JNode node) {
|
||||
ContentPanel panel = openTabs.get(node);
|
||||
if (panel != null) {
|
||||
setTabComponentAt(indexOfComponent(panel), makeTabComponent(panel));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ContentPanel makeContentPanel(JNode node) {
|
||||
if (node instanceof JResource) {
|
||||
|
||||
@@ -53,6 +53,12 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
*/
|
||||
public abstract void load();
|
||||
|
||||
/**
|
||||
* Implement in this method the code that reloads node from cache and sets the new content to be
|
||||
* displayed
|
||||
*/
|
||||
public abstract void refresh();
|
||||
|
||||
public static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) {
|
||||
RSyntaxTextArea area = new RSyntaxTextArea();
|
||||
area.setEditable(false);
|
||||
|
||||
@@ -70,4 +70,8 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel {
|
||||
return javaCodePanel.getCodeArea();
|
||||
}
|
||||
|
||||
public CodePanel getJavaCodePanel() {
|
||||
return javaCodePanel;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
setText(node.getContent());
|
||||
}
|
||||
|
||||
private void addMenuItems() {
|
||||
FindUsageAction findUsage = new FindUsageAction(this);
|
||||
GoToDeclarationAction goToDeclaration = new GoToDeclarationAction(this);
|
||||
|
||||
@@ -87,4 +87,14 @@ public class CodePanel extends JPanel {
|
||||
public JScrollPane getCodeScrollPane() {
|
||||
return codeScrollPane;
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
JViewport viewport = getCodeScrollPane().getViewport();
|
||||
Point viewPosition = viewport.getViewPosition();
|
||||
codeArea.refresh();
|
||||
initLineNumbers();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
viewport.setViewPosition(viewPosition);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,11 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JNode getNode() {
|
||||
// this area contains only smali without other node attributes
|
||||
|
||||
@@ -29,6 +29,12 @@ public class JNodeCache {
|
||||
return jNode;
|
||||
}
|
||||
|
||||
public void refresh(JNode node) {
|
||||
JavaNode javaNode = node.getJavaNode();
|
||||
cache.remove(javaNode);
|
||||
makeFrom(javaNode);
|
||||
}
|
||||
|
||||
private JNode convert(JavaNode node) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user