提交 a3464d71 编写于 作者: S Skylot

fix(gui): make link for full class names (#378)

上级 a8a31643
...@@ -311,10 +311,38 @@ public final class JadxDecompiler { ...@@ -311,10 +311,38 @@ public final class JadxDecompiler {
return methodsMap; return methodsMap;
} }
JavaMethod getJavaMethodByNode(MethodNode mth) {
JavaMethod javaMethod = methodsMap.get(mth);
if (javaMethod != null) {
return javaMethod;
}
// parent class not loaded yet
JavaClass javaClass = classesMap.get(mth.getParentClass());
if (javaClass != null) {
javaClass.decompile();
return methodsMap.get(mth);
}
return null;
}
Map<FieldNode, JavaField> getFieldsMap() { Map<FieldNode, JavaField> getFieldsMap() {
return fieldsMap; return fieldsMap;
} }
JavaField getJavaFieldByNode(FieldNode fld) {
JavaField javaField = fieldsMap.get(fld);
if (javaField != null) {
return javaField;
}
// parent class not loaded yet
JavaClass javaClass = classesMap.get(fld.getParentClass());
if (javaClass != null) {
javaClass.decompile();
return fieldsMap.get(fld);
}
return null;
}
public JadxArgs getArgs() { public JadxArgs getArgs() {
return args; return args;
} }
......
...@@ -160,10 +160,10 @@ public final class JavaClass implements JavaNode { ...@@ -160,10 +160,10 @@ public final class JavaClass implements JavaNode {
return getRootDecompiler().getClassesMap().get(obj); return getRootDecompiler().getClassesMap().get(obj);
} }
if (obj instanceof MethodNode) { if (obj instanceof MethodNode) {
return getRootDecompiler().getMethodsMap().get(obj); return getRootDecompiler().getJavaMethodByNode(((MethodNode) obj));
} }
if (obj instanceof FieldNode) { if (obj instanceof FieldNode) {
return getRootDecompiler().getFieldsMap().get(obj); return getRootDecompiler().getJavaFieldByNode((FieldNode) obj);
} }
return null; return null;
} }
...@@ -181,15 +181,6 @@ public final class JavaClass implements JavaNode { ...@@ -181,15 +181,6 @@ public final class JavaClass implements JavaNode {
return convertNode(obj); return convertNode(obj);
} }
@Nullable
public CodePosition getDefinitionPosition(int line, int offset) {
JavaNode javaNode = getJavaNodeAtPosition(line, offset);
if (javaNode == null) {
return null;
}
return getDefinitionPosition(javaNode);
}
@Nullable @Nullable
public CodePosition getDefinitionPosition(JavaNode javaNode) { public CodePosition getDefinitionPosition(JavaNode javaNode) {
JavaClass jCls = javaNode.getTopParentClass(); JavaClass jCls = javaNode.getTopParentClass();
...@@ -265,6 +256,6 @@ public final class JavaClass implements JavaNode { ...@@ -265,6 +256,6 @@ public final class JavaClass implements JavaNode {
@Override @Override
public String toString() { public String toString() {
return cls.getFullName() + "[ " + getFullName() + " ]"; return getFullName();
} }
} }
package jadx.core.codegen; package jadx.core.codegen;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
...@@ -84,35 +83,18 @@ public class ClassGen { ...@@ -84,35 +83,18 @@ public class ClassGen {
} }
int importsCount = imports.size(); int importsCount = imports.size();
if (importsCount != 0) { if (importsCount != 0) {
List<String> sortImports = new ArrayList<>(importsCount); List<ClassInfo> sortedImports = new ArrayList<>(imports);
for (ClassInfo ic : imports) { sortedImports.sort(Comparator.comparing(classInfo -> classInfo.getAlias().getFullName()));
sortImports.add(ic.getAlias().getFullName()); sortedImports.forEach(classInfo -> {
}
Collections.sort(sortImports);
for (String imp : sortImports) {
ClassInfo importClassInfo = ClassInfo.fromName(cls.dex().root(), imp);
ClassNode classNode = cls.dex().resolveClass(importClassInfo);
// Clickable element seems to be limited by the next dot, therefore
// we can't just use the complete class name including packagename
int clsDotIdx = imp.lastIndexOf('.');
String pkg = "";
if (clsDotIdx >= 0) {
pkg = imp.substring(0, clsDotIdx + 1);
imp = imp.substring(clsDotIdx + 1);
}
clsCode.startLine("import "); clsCode.startLine("import ");
clsCode.add(pkg); ClassNode classNode = cls.root().resolveClass(classInfo);
if (classNode != null) { if (classNode != null) {
// attach the clickable link info to the class name
clsCode.attachAnnotation(classNode); clsCode.attachAnnotation(classNode);
} }
clsCode.add(imp); clsCode.add(classInfo.getAlias().getFullName());
clsCode.add(';'); clsCode.add(';');
} });
clsCode.newLine(); clsCode.newLine();
sortImports.clear();
imports.clear(); imports.clear();
} }
clsCode.add(clsBody); clsCode.add(clsBody);
......
...@@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory; ...@@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs; import jadx.api.JadxArgs;
import jadx.cli.JadxCLIArgs; import jadx.cli.JadxCLIArgs;
import jadx.gui.ui.CodeArea; import jadx.gui.ui.codearea.EditorTheme;
import jadx.gui.utils.LangLocale; import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
...@@ -263,7 +263,7 @@ public class JadxSettings extends JadxCLIArgs { ...@@ -263,7 +263,7 @@ public class JadxSettings extends JadxCLIArgs {
fromVersion++; fromVersion++;
} }
if (fromVersion == 1) { if (fromVersion == 1) {
setEditorThemePath(CodeArea.getAllThemes()[0].getPath()); setEditorThemePath(EditorTheme.ALL_THEMES[0].getPath());
fromVersion++; fromVersion++;
} }
if (fromVersion == 2) { if (fromVersion == 2) {
......
...@@ -12,8 +12,7 @@ import org.slf4j.Logger; ...@@ -12,8 +12,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import say.swing.JFontChooser; import say.swing.JFontChooser;
import jadx.gui.ui.CodeArea; import jadx.gui.ui.codearea.EditorTheme;
import jadx.gui.ui.CodeArea.EditorTheme;
import jadx.gui.ui.MainWindow; import jadx.gui.ui.MainWindow;
import jadx.gui.utils.LangLocale; import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
...@@ -191,7 +190,7 @@ public class JadxSettingsWindow extends JDialog { ...@@ -191,7 +190,7 @@ public class JadxSettingsWindow extends JDialog {
} }
}); });
EditorTheme[] editorThemes = CodeArea.getAllThemes(); EditorTheme[] editorThemes = EditorTheme.ALL_THEMES;
JComboBox<EditorTheme> themesCbx = new JComboBox<>(editorThemes); JComboBox<EditorTheme> themesCbx = new JComboBox<>(editorThemes);
for (EditorTheme theme : editorThemes) { for (EditorTheme theme : editorThemes) {
if (theme.getPath().equals(settings.getEditorThemePath())) { if (theme.getPath().equals(settings.getEditorThemePath())) {
......
...@@ -6,6 +6,7 @@ import java.awt.*; ...@@ -6,6 +6,7 @@ import java.awt.*;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.ui.codearea.CodeArea;
public final class CertificatePanel extends ContentPanel { public final class CertificatePanel extends ContentPanel {
private static final long serialVersionUID = 8566591625905036877L; private static final long serialVersionUID = 8566591625905036877L;
......
...@@ -31,9 +31,10 @@ import jadx.gui.jobs.BackgroundJob; ...@@ -31,9 +31,10 @@ import jadx.gui.jobs.BackgroundJob;
import jadx.gui.jobs.BackgroundWorker; import jadx.gui.jobs.BackgroundWorker;
import jadx.gui.jobs.DecompileJob; import jadx.gui.jobs.DecompileJob;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.ui.codearea.CodeArea;
import jadx.gui.utils.CacheObject; import jadx.gui.utils.CacheObject;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Position; import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.search.TextSearchIndex; import jadx.gui.utils.search.TextSearchIndex;
public abstract class CommonSearchDialog extends JDialog { public abstract class CommonSearchDialog extends JDialog {
...@@ -106,7 +107,7 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -106,7 +107,7 @@ public abstract class CommonSearchDialog extends JDialog {
return; return;
} }
JNode node = (JNode) resultsModel.getValueAt(selectedId, 0); JNode node = (JNode) resultsModel.getValueAt(selectedId, 0);
tabbedPane.codeJump(new Position(node.getRootClass(), node.getLine())); tabbedPane.codeJump(new JumpPosition(node.getRootClass(), node.getLine()));
dispose(); dispose();
} }
......
...@@ -4,25 +4,25 @@ import javax.swing.*; ...@@ -4,25 +4,25 @@ import javax.swing.*;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
abstract class ContentPanel extends JPanel { public abstract class ContentPanel extends JPanel {
private static final long serialVersionUID = 3237031760631677822L; private static final long serialVersionUID = 3237031760631677822L;
protected final TabbedPane tabbedPane; protected final TabbedPane tabbedPane;
protected final JNode node; protected final JNode node;
ContentPanel(TabbedPane panel, JNode jnode) { protected ContentPanel(TabbedPane panel, JNode jnode) {
tabbedPane = panel; tabbedPane = panel;
node = jnode; node = jnode;
} }
public abstract void loadSettings(); public abstract void loadSettings();
TabbedPane getTabbedPane() { public TabbedPane getTabbedPane() {
return tabbedPane; return tabbedPane;
} }
JNode getNode() { public JNode getNode() {
return node; return node;
} }
} }
...@@ -7,6 +7,7 @@ import ch.qos.logback.classic.Level; ...@@ -7,6 +7,7 @@ import ch.qos.logback.classic.Level;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import jadx.gui.settings.JadxSettings; import jadx.gui.settings.JadxSettings;
import jadx.gui.ui.codearea.CodeArea;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.logs.ILogListener; import jadx.gui.utils.logs.ILogListener;
import jadx.gui.utils.logs.LogCollector; import jadx.gui.utils.logs.LogCollector;
......
...@@ -51,7 +51,7 @@ import jadx.gui.update.data.Release; ...@@ -51,7 +51,7 @@ import jadx.gui.update.data.Release;
import jadx.gui.utils.CacheObject; import jadx.gui.utils.CacheObject;
import jadx.gui.utils.Link; import jadx.gui.utils.Link;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Position; import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
import static javax.swing.KeyStroke.getKeyStroke; import static javax.swing.KeyStroke.getKeyStroke;
...@@ -300,7 +300,7 @@ public class MainWindow extends JFrame { ...@@ -300,7 +300,7 @@ public class MainWindow extends JFrame {
JNode node = (JNode) obj; JNode node = (JNode) obj;
JClass cls = node.getRootClass(); JClass cls = node.getRootClass();
if (cls != null) { if (cls != null) {
tabbedPane.codeJump(new Position(cls, node.getLine())); tabbedPane.codeJump(new JumpPosition(cls, node.getLine()));
} }
} }
} catch (Exception e) { } catch (Exception e) {
......
...@@ -21,12 +21,14 @@ import jadx.gui.treemodel.JCertificate; ...@@ -21,12 +21,14 @@ import jadx.gui.treemodel.JCertificate;
import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource; import jadx.gui.treemodel.JResource;
import jadx.gui.ui.codearea.CodeArea;
import jadx.gui.ui.codearea.CodePanel;
import jadx.gui.utils.JumpManager; import jadx.gui.utils.JumpManager;
import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
class TabbedPane extends JTabbedPane { public class TabbedPane extends JTabbedPane {
private static final Logger LOG = LoggerFactory.getLogger(TabbedPane.class); private static final Logger LOG = LoggerFactory.getLogger(TabbedPane.class);
private static final long serialVersionUID = -8833600618794570904L; private static final long serialVersionUID = -8833600618794570904L;
...@@ -57,11 +59,11 @@ class TabbedPane extends JTabbedPane { ...@@ -57,11 +59,11 @@ class TabbedPane extends JTabbedPane {
}); });
} }
MainWindow getMainWindow() { public MainWindow getMainWindow() {
return mainWindow; return mainWindow;
} }
private void showCode(final Position pos) { private void showCode(final JumpPosition pos) {
final CodePanel contentPanel = (CodePanel) getContentPanel(pos.getNode()); final CodePanel contentPanel = (CodePanel) getContentPanel(pos.getNode());
if (contentPanel == null) { if (contentPanel == null) {
return; return;
...@@ -99,8 +101,8 @@ class TabbedPane extends JTabbedPane { ...@@ -99,8 +101,8 @@ class TabbedPane extends JTabbedPane {
SwingUtilities.invokeLater(() -> setSelectedComponent(contentPanel)); SwingUtilities.invokeLater(() -> setSelectedComponent(contentPanel));
} }
public void codeJump(Position pos) { public void codeJump(JumpPosition pos) {
Position curPos = getCurrentPosition(); JumpPosition curPos = getCurrentPosition();
if (curPos != null) { if (curPos != null) {
jumps.addPosition(curPos); jumps.addPosition(curPos);
jumps.addPosition(pos); jumps.addPosition(pos);
...@@ -109,7 +111,7 @@ class TabbedPane extends JTabbedPane { ...@@ -109,7 +111,7 @@ class TabbedPane extends JTabbedPane {
} }
@Nullable @Nullable
private Position getCurrentPosition() { private JumpPosition getCurrentPosition() {
ContentPanel selectedCodePanel = getSelectedCodePanel(); ContentPanel selectedCodePanel = getSelectedCodePanel();
if (selectedCodePanel instanceof CodePanel) { if (selectedCodePanel instanceof CodePanel) {
return ((CodePanel) selectedCodePanel).getCodeArea().getCurrentPosition(); return ((CodePanel) selectedCodePanel).getCodeArea().getCurrentPosition();
...@@ -118,14 +120,14 @@ class TabbedPane extends JTabbedPane { ...@@ -118,14 +120,14 @@ class TabbedPane extends JTabbedPane {
} }
public void navBack() { public void navBack() {
Position pos = jumps.getPrev(); JumpPosition pos = jumps.getPrev();
if (pos != null) { if (pos != null) {
showCode(pos); showCode(pos);
} }
} }
public void navForward() { public void navForward() {
Position pos = jumps.getNext(); JumpPosition pos = jumps.getNext();
if (pos != null) { if (pos != null) {
showCode(pos); showCode(pos);
} }
......
package jadx.gui.ui; package jadx.gui.ui.codearea;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener; import javax.swing.event.PopupMenuListener;
import javax.swing.text.BadLocationException; import javax.swing.text.BadLocationException;
import javax.swing.text.Caret; import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret; import javax.swing.text.DefaultCaret;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import org.fife.ui.rsyntaxtextarea.LinkGenerator; import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.fife.ui.rtextarea.SearchContext; import org.fife.ui.rtextarea.SearchContext;
import org.fife.ui.rtextarea.SearchEngine; import org.fife.ui.rtextarea.SearchEngine;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
...@@ -29,7 +22,8 @@ import jadx.api.JavaNode; ...@@ -29,7 +22,8 @@ import jadx.api.JavaNode;
import jadx.gui.settings.JadxSettings; import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.utils.Position; import jadx.gui.ui.MainWindow;
import jadx.gui.utils.JumpPosition;
public final class CodeArea extends RSyntaxTextArea { public final class CodeArea extends RSyntaxTextArea {
private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class); private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
...@@ -55,11 +49,14 @@ public final class CodeArea extends RSyntaxTextArea { ...@@ -55,11 +49,14 @@ public final class CodeArea extends RSyntaxTextArea {
setSyntaxEditingStyle(node.getSyntaxName()); setSyntaxEditingStyle(node.getSyntaxName());
if (node instanceof JClass) { if (node instanceof JClass) {
JClass jClsNode = (JClass) this.node;
((RSyntaxDocument) getDocument()).setSyntaxStyle(new JadxTokenMaker(this, jClsNode));
setHyperlinksEnabled(true); setHyperlinksEnabled(true);
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator((JClass) node); CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator(contentPanel, this, jClsNode);
setLinkGenerator(codeLinkProcessor); setLinkGenerator(codeLinkProcessor);
addHyperlinkListener(codeLinkProcessor); addHyperlinkListener(codeLinkProcessor);
addMenuItems(this, (JClass) node); addMenuItems(jClsNode);
} }
registerWordHighlighter(); registerWordHighlighter();
setText(node.getContent()); setText(node.getContent());
...@@ -93,8 +90,8 @@ public final class CodeArea extends RSyntaxTextArea { ...@@ -93,8 +90,8 @@ public final class CodeArea extends RSyntaxTextArea {
SearchEngine.markAll(this, context); SearchEngine.markAll(this, context);
} }
private void addMenuItems(CodeArea codeArea, JClass jCls) { private void addMenuItems(JClass jCls) {
Action findUsage = new FindUsageAction(codeArea, jCls); Action findUsage = new FindUsageAction(contentPanel, this, jCls);
JPopupMenu popup = getPopupMenu(); JPopupMenu popup = getPopupMenu();
popup.addSeparator(); popup.addSeparator();
...@@ -120,75 +117,48 @@ public final class CodeArea extends RSyntaxTextArea { ...@@ -120,75 +117,48 @@ public final class CodeArea extends RSyntaxTextArea {
return area; return area;
} }
private boolean isJumpToken(Token token) { /**
if (token.getType() == TokenTypes.IDENTIFIER) { * Search node by offset in {@code jCls} code and return its definition position
// fast skip * (useful for jumps from usage)
if (token.length() == 1) { */
char ch = token.getTextArray()[token.getTextOffset()]; public JumpPosition getDefPosForNodeAtOffset(JClass jCls, int offset) {
if (ch == '.' || ch == ',' || ch == ';') { JavaNode foundNode = getJavaNodeAtOffset(jCls, offset);
return false; if (foundNode == null) {
}
}
if (node instanceof JClass) {
Position pos = getDefPosition((JClass) node, this, token.getOffset());
if (pos != null) {
// don't underline definition place
try {
int defLine = pos.getLine();
int lineOfOffset = getLineOfOffset(token.getOffset()) + 1;
if (defLine == lineOfOffset) {
return false;
}
} catch (BadLocationException e) {
return false;
}
return true;
}
}
}
return false;
}
// @Override
// public Color getForegroundForToken(Token t) {
// if (isJumpToken(t)) {
// return getHyperlinkForeground();
// }
// return super.getForegroundForToken(t);
// }
static Position getDefPosition(JClass jCls, RSyntaxTextArea textArea, int offset) {
JavaNode node = getJavaNodeAtOffset(jCls, textArea, offset);
if (node == null) {
return null; return null;
} }
CodePosition pos = jCls.getCls().getDefinitionPosition(node); CodePosition pos = jCls.getCls().getDefinitionPosition(foundNode);
if (pos == null) { if (pos == null) {
return null; return null;
} }
return new Position(pos); JNode jNode = contentPanel.getTabbedPane().getMainWindow().getCacheObject().getNodeCache().makeFrom(foundNode);
return new JumpPosition(jNode.getRootClass(), pos.getLine());
} }
static JavaNode getJavaNodeAtOffset(JClass jCls, RSyntaxTextArea textArea, int offset) { /**
* Search referenced java node by offset in {@code jCls} code
*/
public JavaNode getJavaNodeAtOffset(JClass jCls, int offset) {
try { try {
int line = textArea.getLineOfOffset(offset); // TODO: add direct mapping for code offset to CodeWriter (instead of line and line offset pair)
int lineOffset = offset - textArea.getLineStartOffset(line); int line = this.getLineOfOffset(offset);
int lineOffset = offset - this.getLineStartOffset(line);
return jCls.getCls().getJavaNodeAtPosition(line + 1, lineOffset + 1); return jCls.getCls().getJavaNodeAtPosition(line + 1, lineOffset + 1);
} catch (BadLocationException e) { } catch (Exception e) {
LOG.error("Can't get java node by offset", e); LOG.error("Can't get java node by offset: {}", offset, e);
} }
return null; return null;
} }
public Position getCurrentPosition() { public JumpPosition getCurrentPosition() {
return new Position(node, getCaretLineNumber() + 1); return new JumpPosition(node, getCaretLineNumber() + 1);
} }
@Nullable
Integer getSourceLine(int line) { Integer getSourceLine(int line) {
return node.getSourceLine(line); return node.getSourceLine(line);
} }
void scrollToLine(int line) { public void scrollToLine(int line) {
int lineNum = line - 1; int lineNum = line - 1;
if (lineNum < 0) { if (lineNum < 0) {
lineNum = 0; lineNum = 0;
...@@ -231,131 +201,4 @@ public final class CodeArea extends RSyntaxTextArea { ...@@ -231,131 +201,4 @@ public final class CodeArea extends RSyntaxTextArea {
LOG.debug("Can't scroll to {}", line, e); LOG.debug("Can't scroll to {}", line, e);
} }
} }
private class FindUsageAction extends AbstractAction implements PopupMenuListener {
private static final long serialVersionUID = 4692546569977976384L;
private final transient CodeArea codeArea;
private final transient JClass jCls;
private transient JavaNode node;
public FindUsageAction(CodeArea codeArea, JClass jCls) {
super("Find Usage");
this.codeArea = codeArea;
this.jCls = jCls;
}
@Override
public void actionPerformed(ActionEvent e) {
if (node == null) {
return;
}
MainWindow mainWindow = contentPanel.getTabbedPane().getMainWindow();
JNode jNode = mainWindow.getCacheObject().getNodeCache().makeFrom(node);
UsageDialog usageDialog = new UsageDialog(mainWindow, jNode);
usageDialog.setVisible(true);
}
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
node = null;
Point pos = codeArea.getMousePosition();
if (pos != null) {
Token token = codeArea.viewToToken(pos);
if (token != null) {
node = getJavaNodeAtOffset(jCls, codeArea, token.getOffset());
}
}
setEnabled(node != null);
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
}
private class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
private final JClass jCls;
public CodeLinkGenerator(JClass cls) {
this.jCls = cls;
}
@Override
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) {
try {
Token token = textArea.modelToToken(offset);
if (token == null) {
return null;
}
final int sourceOffset = token.getOffset();
final Position defPos = getDefPosition(jCls, textArea, sourceOffset);
if (defPos == null) {
return null;
}
return new LinkGeneratorResult() {
@Override
public HyperlinkEvent execute() {
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
defPos.getNode().makeLongString());
}
@Override
public int getSourceOffset() {
return sourceOffset;
}
};
} catch (Exception e) {
LOG.error("isLinkAtOffset error", e);
return null;
}
}
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
Object obj = e.getSource();
if (obj instanceof Position) {
contentPanel.getTabbedPane().codeJump((Position) obj);
}
}
}
public static final class EditorTheme {
private final String name;
private final String path;
public EditorTheme(String name, String path) {
this.name = name;
this.path = path;
}
public String getName() {
return name;
}
public String getPath() {
return path;
}
@Override
public String toString() {
return name;
}
}
public static EditorTheme[] getAllThemes() {
return new EditorTheme[]{
new EditorTheme("default", "/org/fife/ui/rsyntaxtextarea/themes/default.xml"),
new EditorTheme("eclipse", "/org/fife/ui/rsyntaxtextarea/themes/eclipse.xml"),
new EditorTheme("idea", "/org/fife/ui/rsyntaxtextarea/themes/idea.xml"),
new EditorTheme("vs", "/org/fife/ui/rsyntaxtextarea/themes/vs.xml"),
new EditorTheme("dark", "/org/fife/ui/rsyntaxtextarea/themes/dark.xml"),
new EditorTheme("monokai", "/org/fife/ui/rsyntaxtextarea/themes/monokai.xml")
};
}
} }
package jadx.gui.ui.codearea;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.util.Objects;
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.gui.treemodel.JClass;
import jadx.gui.utils.JumpPosition;
public class CodeLinkGenerator implements LinkGenerator, HyperlinkListener {
private static final Logger LOG = LoggerFactory.getLogger(CodeLinkGenerator.class);
private final CodePanel contentPanel;
private final CodeArea codeArea;
private final JClass jCls;
public CodeLinkGenerator(CodePanel contentPanel, CodeArea codeArea, JClass cls) {
this.contentPanel = contentPanel;
this.codeArea = codeArea;
this.jCls = cls;
}
@Override
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) {
try {
Token token = textArea.modelToToken(offset);
if (token == null) {
return null;
}
int type = token.getType();
final int sourceOffset;
if (type == TokenTypes.IDENTIFIER) {
sourceOffset = token.getOffset();
} else if (type == TokenTypes.ANNOTATION && token.length() > 1) {
sourceOffset = token.getOffset() + 1;
} else {
return null;
}
// fast skip
if (token.length() == 1) {
char ch = token.getTextArray()[token.getTextOffset()];
if (ch == '.' || ch == ',' || ch == ';') {
return null;
}
}
final JumpPosition defPos = codeArea.getDefPosForNodeAtOffset(jCls, sourceOffset);
if (defPos == null) {
return null;
}
if (Objects.equals(defPos.getNode().getRootClass(), jCls)
&& defPos.getLine() == textArea.getLineOfOffset(sourceOffset) + 1) {
// ignore self jump
return null;
}
return new LinkGeneratorResult() {
@Override
public HyperlinkEvent execute() {
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
defPos.getNode().makeLongString());
}
@Override
public int getSourceOffset() {
return sourceOffset;
}
};
} catch (Exception e) {
LOG.error("isLinkAtOffset error", e);
return null;
}
}
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
Object obj = e.getSource();
if (obj instanceof JumpPosition) {
contentPanel.getTabbedPane().codeJump((JumpPosition) obj);
}
}
}
package jadx.gui.ui; package jadx.gui.ui.codearea;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
...@@ -7,9 +7,11 @@ import java.awt.event.InputEvent; ...@@ -7,9 +7,11 @@ import java.awt.event.InputEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.TabbedPane;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
class CodePanel extends ContentPanel { public final class CodePanel extends ContentPanel {
private static final long serialVersionUID = 5310536092010045565L; private static final long serialVersionUID = 5310536092010045565L;
...@@ -17,7 +19,7 @@ class CodePanel extends ContentPanel { ...@@ -17,7 +19,7 @@ class CodePanel extends ContentPanel {
private final CodeArea codeArea; private final CodeArea codeArea;
private final JScrollPane scrollPane; private final JScrollPane scrollPane;
CodePanel(TabbedPane panel, JNode jnode) { public CodePanel(TabbedPane panel, JNode jnode) {
super(panel, jnode); super(panel, jnode);
codeArea = new CodeArea(this); codeArea = new CodeArea(this);
...@@ -55,12 +57,12 @@ class CodePanel extends ContentPanel { ...@@ -55,12 +57,12 @@ class CodePanel extends ContentPanel {
} }
@Override @Override
TabbedPane getTabbedPane() { public TabbedPane getTabbedPane() {
return tabbedPane; return tabbedPane;
} }
@Override @Override
JNode getNode() { public JNode getNode() {
return node; return node;
} }
...@@ -68,7 +70,7 @@ class CodePanel extends ContentPanel { ...@@ -68,7 +70,7 @@ class CodePanel extends ContentPanel {
return searchBar; return searchBar;
} }
CodeArea getCodeArea() { public CodeArea getCodeArea() {
return codeArea; return codeArea;
} }
......
package jadx.gui.ui.codearea;
public final class EditorTheme {
private static final String RSTA_THEME_PATH = "/org/fife/ui/rsyntaxtextarea/themes/";
public static final EditorTheme[] ALL_THEMES =
new EditorTheme[]{
new EditorTheme("default"),
new EditorTheme("eclipse"),
new EditorTheme("idea"),
new EditorTheme("vs"),
new EditorTheme("dark"),
new EditorTheme("monokai")
};
private final String name;
private final String path;
public EditorTheme(String name) {
this(name, RSTA_THEME_PATH + name + ".xml");
}
public EditorTheme(String name, String path) {
this.name = name;
this.path = path;
}
public String getName() {
return name;
}
public String getPath() {
return path;
}
@Override
public String toString() {
return name;
}
}
package jadx.gui.ui.codearea;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import org.fife.ui.rsyntaxtextarea.Token;
import jadx.api.JavaNode;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.UsageDialog;
import jadx.gui.utils.NLS;
public final class FindUsageAction extends AbstractAction implements PopupMenuListener {
private static final long serialVersionUID = 4692546569977976384L;
private final transient CodePanel contentPanel;
private final transient CodeArea codeArea;
private final transient JClass jCls;
private transient JavaNode node;
public FindUsageAction(CodePanel contentPanel, CodeArea codeArea, JClass jCls) {
super(NLS.str("popup.find_usage"));
this.contentPanel = contentPanel;
this.codeArea = codeArea;
this.jCls = jCls;
}
@Override
public void actionPerformed(ActionEvent e) {
if (node == null) {
return;
}
MainWindow mainWindow = contentPanel.getTabbedPane().getMainWindow();
JNode jNode = mainWindow.getCacheObject().getNodeCache().makeFrom(node);
UsageDialog usageDialog = new UsageDialog(mainWindow, jNode);
usageDialog.setVisible(true);
}
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
node = null;
Point pos = codeArea.getMousePosition();
if (pos != null) {
Token token = codeArea.viewToToken(pos);
if (token != null) {
node = codeArea.getJavaNodeAtOffset(jCls, token.getOffset());
}
}
setEnabled(node != null);
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
// do nothing
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
// do nothing
}
}
package jadx.gui.ui.codearea;
import javax.swing.text.Segment;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rsyntaxtextarea.TokenImpl;
import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.fife.ui.rsyntaxtextarea.modes.JavaTokenMaker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.JavaClass;
import jadx.api.JavaNode;
import jadx.gui.treemodel.JClass;
public final class JadxTokenMaker extends JavaTokenMaker {
private static final Logger LOG = LoggerFactory.getLogger(JadxTokenMaker.class);
private final CodeArea codeArea;
private final JClass jCls;
public JadxTokenMaker(CodeArea codeArea, JClass jCls) {
this.codeArea = codeArea;
this.jCls = jCls;
}
@Override
public Token getTokenList(Segment text, int initialTokenType, int startOffset) {
Token tokens = super.getTokenList(text, initialTokenType, startOffset);
if (startOffset > 0 && tokens.getType() != TokenTypes.NULL) {
try {
processTokens(tokens);
} catch (Exception e) {
LOG.error("Process tokens failed for text: {}", text, e);
}
}
return tokens;
}
private void processTokens(Token tokens) {
Token prev = null;
Token current = tokens;
while (current != null) {
if (prev != null) {
int tokenType = current.getType();
if (tokenType == TokenTypes.IDENTIFIER) {
current = mergeLongClassNames(prev, current, false);
} else if (tokenType == TokenTypes.ANNOTATION) {
current = mergeLongClassNames(prev, current, true);
}
}
prev = current;
current = current.getNextToken();
}
}
@NotNull
private Token mergeLongClassNames(Token prev, Token current, boolean annotation) {
int offset = current.getOffset();
if (annotation) {
offset++;
}
JavaNode javaNode = codeArea.getJavaNodeAtOffset(jCls, offset);
if (javaNode instanceof JavaClass) {
String name = javaNode.getName();
String lexeme = current.getLexeme();
if (annotation && lexeme.length() > 1) {
lexeme = lexeme.substring(1);
}
if (!lexeme.equals(name) && javaNode.getFullName().startsWith(lexeme)) {
// try to replace long class name with one token
Token replace = concatTokensUntil(current, name);
if (replace != null && prev instanceof TokenImpl) {
TokenImpl impl = ((TokenImpl) prev);
impl.setNextToken(replace);
current = replace;
}
}
}
return current;
}
@Nullable
private Token concatTokensUntil(Token start, String endText) {
StringBuilder sb = new StringBuilder();
Token current = start;
while (current != null && current.getType() != TokenTypes.NULL) {
String text = current.getLexeme();
if (text != null) {
sb.append(text);
if (text.equals(endText)) {
char[] line = sb.toString().toCharArray();
TokenImpl token = new TokenImpl(line, 0, line.length - 1, start.getOffset(),
start.getType(), start.getLanguageIndex());
token.setNextToken(current.getNextToken());
return token;
}
}
current = current.getNextToken();
}
return null;
}
}
package jadx.gui.ui; package jadx.gui.ui.codearea;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
......
package jadx.gui.ui; package jadx.gui.ui.codearea;
import javax.swing.*; import javax.swing.*;
import javax.swing.text.BadLocationException; import javax.swing.text.BadLocationException;
...@@ -112,12 +112,7 @@ class SearchBar extends JToolBar { ...@@ -112,12 +112,7 @@ class SearchBar extends JToolBar {
JButton closeButton = new JButton(); JButton closeButton = new JButton();
closeButton.setIcon(ICON_CLOSE); closeButton.setIcon(ICON_CLOSE);
closeButton.addActionListener(new ActionListener() { closeButton.addActionListener(l -> toggle());
@Override
public void actionPerformed(ActionEvent e) {
toggle();
}
});
closeButton.setBorderPainted(false); closeButton.setBorderPainted(false);
add(closeButton); add(closeButton);
......
...@@ -5,10 +5,10 @@ import java.util.List; ...@@ -5,10 +5,10 @@ import java.util.List;
public class JumpManager { public class JumpManager {
private List<Position> list = new ArrayList<>(); private List<JumpPosition> list = new ArrayList<>();
private int currentPos = 0; private int currentPos = 0;
public void addPosition(Position pos) { public void addPosition(JumpPosition pos) {
if (pos.equals(getCurrent())) { if (pos.equals(getCurrent())) {
return; return;
} }
...@@ -25,14 +25,14 @@ public class JumpManager { ...@@ -25,14 +25,14 @@ public class JumpManager {
} }
} }
private Position getCurrent() { private JumpPosition getCurrent() {
if (currentPos >= 0 && currentPos < list.size()) { if (currentPos >= 0 && currentPos < list.size()) {
return list.get(currentPos); return list.get(currentPos);
} }
return null; return null;
} }
public Position getPrev() { public JumpPosition getPrev() {
if (currentPos == 0) { if (currentPos == 0) {
return null; return null;
} }
...@@ -40,7 +40,7 @@ public class JumpManager { ...@@ -40,7 +40,7 @@ public class JumpManager {
return list.get(currentPos); return list.get(currentPos);
} }
public Position getNext() { public JumpPosition getNext() {
int size = list.size(); int size = list.size();
if (size == 0) { if (size == 0) {
currentPos = 0; currentPos = 0;
...@@ -51,7 +51,7 @@ public class JumpManager { ...@@ -51,7 +51,7 @@ public class JumpManager {
currentPos = size - 1; currentPos = size - 1;
return null; return null;
} }
Position position = list.get(newPos); JumpPosition position = list.get(newPos);
if (position == null) { if (position == null) {
return null; return null;
} }
......
package jadx.gui.utils; package jadx.gui.utils;
import jadx.api.CodePosition;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
public class Position { public class JumpPosition {
private final JNode node; private final JNode node;
private final int line; private final int line;
public Position(CodePosition pos) { public JumpPosition(JNode node, int line) {
this.node = new JClass(pos.getJavaClass());
this.line = pos.getLine();
}
public Position(JNode node, int line) {
this.node = node; this.node = node;
this.line = line; this.line = line;
} }
...@@ -31,10 +24,10 @@ public class Position { ...@@ -31,10 +24,10 @@ public class Position {
if (this == obj) { if (this == obj) {
return true; return true;
} }
if (!(obj instanceof Position)) { if (!(obj instanceof JumpPosition)) {
return false; return false;
} }
Position position = (Position) obj; JumpPosition position = (JumpPosition) obj;
return line == position.line && node.equals(position.node); return line == position.line && node.equals(position.node);
} }
......
...@@ -114,6 +114,7 @@ popup.paste=Paste ...@@ -114,6 +114,7 @@ popup.paste=Paste
popup.delete=Delete popup.delete=Delete
popup.select_all=Select All popup.select_all=Select All
popup.find_usage=Find Usage
certificate.title=Certificate certificate.title=Certificate
certificate.cert_type=Type certificate.cert_type=Type
certificate.serialSigVer=Version certificate.serialSigVer=Version
......
package jadx.gui.tests package jadx.gui.tests
import jadx.gui.utils.JumpManager import jadx.gui.utils.JumpManager
import jadx.gui.utils.Position import jadx.gui.utils.JumpPosition
import spock.lang.Specification import spock.lang.Specification
class TestJumpManager extends Specification { class TestJumpManager extends Specification {
...@@ -29,7 +29,7 @@ class TestJumpManager extends Specification { ...@@ -29,7 +29,7 @@ class TestJumpManager extends Specification {
def "1 element"() { def "1 element"() {
when: when:
jm.addPosition(Mock(Position)) jm.addPosition(Mock(JumpPosition))
then: then:
jm.getPrev() == null jm.getPrev() == null
jm.getNext() == null jm.getNext() == null
...@@ -37,9 +37,9 @@ class TestJumpManager extends Specification { ...@@ -37,9 +37,9 @@ class TestJumpManager extends Specification {
def "2 elements"() { def "2 elements"() {
when: when:
def mock1 = Mock(Position) def mock1 = Mock(JumpPosition)
jm.addPosition(mock1) jm.addPosition(mock1)
def mock2 = Mock(Position) def mock2 = Mock(JumpPosition)
jm.addPosition(mock2) jm.addPosition(mock2)
// 1 - 2@ // 1 - 2@
then: then:
...@@ -52,15 +52,15 @@ class TestJumpManager extends Specification { ...@@ -52,15 +52,15 @@ class TestJumpManager extends Specification {
def "navigation"() { def "navigation"() {
expect: expect:
def mock1 = Mock(Position) def mock1 = Mock(JumpPosition)
jm.addPosition(mock1) jm.addPosition(mock1)
// 1@ // 1@
def mock2 = Mock(Position) def mock2 = Mock(JumpPosition)
jm.addPosition(mock2) jm.addPosition(mock2)
// 1 - 2@ // 1 - 2@
jm.getPrev() == mock1 jm.getPrev() == mock1
// 1@ - 2 // 1@ - 2
def mock3 = Mock(Position) def mock3 = Mock(JumpPosition)
jm.addPosition(mock3) jm.addPosition(mock3)
// 1 - 3@ // 1 - 3@
jm.getNext() == null jm.getNext() == null
...@@ -71,23 +71,23 @@ class TestJumpManager extends Specification { ...@@ -71,23 +71,23 @@ class TestJumpManager extends Specification {
def "navigation2"() { def "navigation2"() {
expect: expect:
def mock1 = Mock(Position) def mock1 = Mock(JumpPosition)
jm.addPosition(mock1) jm.addPosition(mock1)
// 1@ // 1@
def mock2 = Mock(Position) def mock2 = Mock(JumpPosition)
jm.addPosition(mock2) jm.addPosition(mock2)
// 1 - 2@ // 1 - 2@
def mock3 = Mock(Position) def mock3 = Mock(JumpPosition)
jm.addPosition(mock3) jm.addPosition(mock3)
// 1 - 2 - 3@ // 1 - 2 - 3@
def mock4 = Mock(Position) def mock4 = Mock(JumpPosition)
jm.addPosition(mock4) jm.addPosition(mock4)
// 1 - 2 - 3 - 4@ // 1 - 2 - 3 - 4@
jm.getPrev() == mock3 jm.getPrev() == mock3
// 1 - 2 - 3@ - 4 // 1 - 2 - 3@ - 4
jm.getPrev() == mock2 jm.getPrev() == mock2
// 1 - 2@ - 3 - 4 // 1 - 2@ - 3 - 4
def mock5 = Mock(Position) def mock5 = Mock(JumpPosition)
jm.addPosition(mock5) jm.addPosition(mock5)
// 1 - 2 - 5@ // 1 - 2 - 5@
jm.getNext() == null jm.getNext() == null
...@@ -106,7 +106,7 @@ class TestJumpManager extends Specification { ...@@ -106,7 +106,7 @@ class TestJumpManager extends Specification {
def "add same element"() { def "add same element"() {
when: when:
def mock = Mock(Position) def mock = Mock(JumpPosition)
jm.addPosition(mock) jm.addPosition(mock)
jm.addPosition(mock) jm.addPosition(mock)
then: then:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册