提交 a3464d71 编写于 作者: S Skylot

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

上级 a8a31643
......@@ -311,10 +311,38 @@ public final class JadxDecompiler {
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() {
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() {
return args;
}
......
......@@ -160,10 +160,10 @@ public final class JavaClass implements JavaNode {
return getRootDecompiler().getClassesMap().get(obj);
}
if (obj instanceof MethodNode) {
return getRootDecompiler().getMethodsMap().get(obj);
return getRootDecompiler().getJavaMethodByNode(((MethodNode) obj));
}
if (obj instanceof FieldNode) {
return getRootDecompiler().getFieldsMap().get(obj);
return getRootDecompiler().getJavaFieldByNode((FieldNode) obj);
}
return null;
}
......@@ -181,15 +181,6 @@ public final class JavaClass implements JavaNode {
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
public CodePosition getDefinitionPosition(JavaNode javaNode) {
JavaClass jCls = javaNode.getTopParentClass();
......@@ -265,6 +256,6 @@ public final class JavaClass implements JavaNode {
@Override
public String toString() {
return cls.getFullName() + "[ " + getFullName() + " ]";
return getFullName();
}
}
package jadx.core.codegen;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
......@@ -84,35 +83,18 @@ public class ClassGen {
}
int importsCount = imports.size();
if (importsCount != 0) {
List<String> sortImports = new ArrayList<>(importsCount);
for (ClassInfo ic : imports) {
sortImports.add(ic.getAlias().getFullName());
}
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);
}
List<ClassInfo> sortedImports = new ArrayList<>(imports);
sortedImports.sort(Comparator.comparing(classInfo -> classInfo.getAlias().getFullName()));
sortedImports.forEach(classInfo -> {
clsCode.startLine("import ");
clsCode.add(pkg);
ClassNode classNode = cls.root().resolveClass(classInfo);
if (classNode != null) {
// attach the clickable link info to the class name
clsCode.attachAnnotation(classNode);
}
clsCode.add(imp);
clsCode.add(classInfo.getAlias().getFullName());
clsCode.add(';');
}
});
clsCode.newLine();
sortImports.clear();
imports.clear();
}
clsCode.add(clsBody);
......
......@@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory;
import jadx.api.JadxArgs;
import jadx.cli.JadxCLIArgs;
import jadx.gui.ui.CodeArea;
import jadx.gui.ui.codearea.EditorTheme;
import jadx.gui.utils.LangLocale;
import jadx.gui.utils.NLS;
......@@ -263,7 +263,7 @@ public class JadxSettings extends JadxCLIArgs {
fromVersion++;
}
if (fromVersion == 1) {
setEditorThemePath(CodeArea.getAllThemes()[0].getPath());
setEditorThemePath(EditorTheme.ALL_THEMES[0].getPath());
fromVersion++;
}
if (fromVersion == 2) {
......
......@@ -12,8 +12,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.utils.LangLocale;
import jadx.gui.utils.NLS;
......@@ -191,7 +190,7 @@ public class JadxSettingsWindow extends JDialog {
}
});
EditorTheme[] editorThemes = CodeArea.getAllThemes();
EditorTheme[] editorThemes = EditorTheme.ALL_THEMES;
JComboBox<EditorTheme> themesCbx = new JComboBox<>(editorThemes);
for (EditorTheme theme : editorThemes) {
if (theme.getPath().equals(settings.getEditorThemePath())) {
......
......@@ -6,6 +6,7 @@ import java.awt.*;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.codearea.CodeArea;
public final class CertificatePanel extends ContentPanel {
private static final long serialVersionUID = 8566591625905036877L;
......
......@@ -31,9 +31,10 @@ import jadx.gui.jobs.BackgroundJob;
import jadx.gui.jobs.BackgroundWorker;
import jadx.gui.jobs.DecompileJob;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.codearea.CodeArea;
import jadx.gui.utils.CacheObject;
import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.search.TextSearchIndex;
public abstract class CommonSearchDialog extends JDialog {
......@@ -106,7 +107,7 @@ public abstract class CommonSearchDialog extends JDialog {
return;
}
JNode node = (JNode) resultsModel.getValueAt(selectedId, 0);
tabbedPane.codeJump(new Position(node.getRootClass(), node.getLine()));
tabbedPane.codeJump(new JumpPosition(node.getRootClass(), node.getLine()));
dispose();
}
......
......@@ -4,25 +4,25 @@ import javax.swing.*;
import jadx.gui.treemodel.JNode;
abstract class ContentPanel extends JPanel {
public abstract class ContentPanel extends JPanel {
private static final long serialVersionUID = 3237031760631677822L;
protected final TabbedPane tabbedPane;
protected final JNode node;
ContentPanel(TabbedPane panel, JNode jnode) {
protected ContentPanel(TabbedPane panel, JNode jnode) {
tabbedPane = panel;
node = jnode;
}
public abstract void loadSettings();
TabbedPane getTabbedPane() {
public TabbedPane getTabbedPane() {
return tabbedPane;
}
JNode getNode() {
public JNode getNode() {
return node;
}
}
......@@ -7,6 +7,7 @@ import ch.qos.logback.classic.Level;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import jadx.gui.settings.JadxSettings;
import jadx.gui.ui.codearea.CodeArea;
import jadx.gui.utils.NLS;
import jadx.gui.utils.logs.ILogListener;
import jadx.gui.utils.logs.LogCollector;
......
......@@ -51,7 +51,7 @@ import jadx.gui.update.data.Release;
import jadx.gui.utils.CacheObject;
import jadx.gui.utils.Link;
import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
import jadx.gui.utils.JumpPosition;
import jadx.gui.utils.Utils;
import static javax.swing.KeyStroke.getKeyStroke;
......@@ -300,7 +300,7 @@ public class MainWindow extends JFrame {
JNode node = (JNode) obj;
JClass cls = node.getRootClass();
if (cls != null) {
tabbedPane.codeJump(new Position(cls, node.getLine()));
tabbedPane.codeJump(new JumpPosition(cls, node.getLine()));
}
}
} catch (Exception e) {
......
......@@ -21,12 +21,14 @@ import jadx.gui.treemodel.JCertificate;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
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.JumpPosition;
import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
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 long serialVersionUID = -8833600618794570904L;
......@@ -57,11 +59,11 @@ class TabbedPane extends JTabbedPane {
});
}
MainWindow getMainWindow() {
public MainWindow getMainWindow() {
return mainWindow;
}
private void showCode(final Position pos) {
private void showCode(final JumpPosition pos) {
final CodePanel contentPanel = (CodePanel) getContentPanel(pos.getNode());
if (contentPanel == null) {
return;
......@@ -99,8 +101,8 @@ class TabbedPane extends JTabbedPane {
SwingUtilities.invokeLater(() -> setSelectedComponent(contentPanel));
}
public void codeJump(Position pos) {
Position curPos = getCurrentPosition();
public void codeJump(JumpPosition pos) {
JumpPosition curPos = getCurrentPosition();
if (curPos != null) {
jumps.addPosition(curPos);
jumps.addPosition(pos);
......@@ -109,7 +111,7 @@ class TabbedPane extends JTabbedPane {
}
@Nullable
private Position getCurrentPosition() {
private JumpPosition getCurrentPosition() {
ContentPanel selectedCodePanel = getSelectedCodePanel();
if (selectedCodePanel instanceof CodePanel) {
return ((CodePanel) selectedCodePanel).getCodeArea().getCurrentPosition();
......@@ -118,14 +120,14 @@ class TabbedPane extends JTabbedPane {
}
public void navBack() {
Position pos = jumps.getPrev();
JumpPosition pos = jumps.getPrev();
if (pos != null) {
showCode(pos);
}
}
public void navForward() {
Position pos = jumps.getNext();
JumpPosition pos = jumps.getNext();
if (pos != null) {
showCode(pos);
}
......
package jadx.gui.ui;
package jadx.gui.ui.codearea;
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.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
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.SearchEngine;
import org.jetbrains.annotations.Nullable;
......@@ -29,7 +22,8 @@ import jadx.api.JavaNode;
import jadx.gui.settings.JadxSettings;
import jadx.gui.treemodel.JClass;
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 {
private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
......@@ -55,11 +49,14 @@ public final class CodeArea extends RSyntaxTextArea {
setSyntaxEditingStyle(node.getSyntaxName());
if (node instanceof JClass) {
JClass jClsNode = (JClass) this.node;
((RSyntaxDocument) getDocument()).setSyntaxStyle(new JadxTokenMaker(this, jClsNode));
setHyperlinksEnabled(true);
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator((JClass) node);
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator(contentPanel, this, jClsNode);
setLinkGenerator(codeLinkProcessor);
addHyperlinkListener(codeLinkProcessor);
addMenuItems(this, (JClass) node);
addMenuItems(jClsNode);
}
registerWordHighlighter();
setText(node.getContent());
......@@ -93,8 +90,8 @@ public final class CodeArea extends RSyntaxTextArea {
SearchEngine.markAll(this, context);
}
private void addMenuItems(CodeArea codeArea, JClass jCls) {
Action findUsage = new FindUsageAction(codeArea, jCls);
private void addMenuItems(JClass jCls) {
Action findUsage = new FindUsageAction(contentPanel, this, jCls);
JPopupMenu popup = getPopupMenu();
popup.addSeparator();
......@@ -120,75 +117,48 @@ public final class CodeArea extends RSyntaxTextArea {
return area;
}
private boolean isJumpToken(Token token) {
if (token.getType() == TokenTypes.IDENTIFIER) {
// fast skip
if (token.length() == 1) {
char ch = token.getTextArray()[token.getTextOffset()];
if (ch == '.' || ch == ',' || ch == ';') {
return false;
}
}
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) {
/**
* Search node by offset in {@code jCls} code and return its definition position
* (useful for jumps from usage)
*/
public JumpPosition getDefPosForNodeAtOffset(JClass jCls, int offset) {
JavaNode foundNode = getJavaNodeAtOffset(jCls, offset);
if (foundNode == null) {
return null;
}
CodePosition pos = jCls.getCls().getDefinitionPosition(node);
CodePosition pos = jCls.getCls().getDefinitionPosition(foundNode);
if (pos == 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 {
int line = textArea.getLineOfOffset(offset);
int lineOffset = offset - textArea.getLineStartOffset(line);
// TODO: add direct mapping for code offset to CodeWriter (instead of line and line offset pair)
int line = this.getLineOfOffset(offset);
int lineOffset = offset - this.getLineStartOffset(line);
return jCls.getCls().getJavaNodeAtPosition(line + 1, lineOffset + 1);
} catch (BadLocationException e) {
LOG.error("Can't get java node by offset", e);
} catch (Exception e) {
LOG.error("Can't get java node by offset: {}", offset, e);
}
return null;
}
public Position getCurrentPosition() {
return new Position(node, getCaretLineNumber() + 1);
public JumpPosition getCurrentPosition() {
return new JumpPosition(node, getCaretLineNumber() + 1);
}
@Nullable
Integer getSourceLine(int line) {
return node.getSourceLine(line);
}
void scrollToLine(int line) {
public void scrollToLine(int line) {
int lineNum = line - 1;
if (lineNum < 0) {
lineNum = 0;
......@@ -231,131 +201,4 @@ public final class CodeArea extends RSyntaxTextArea {
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 java.awt.*;
......@@ -7,9 +7,11 @@ import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import jadx.gui.treemodel.JNode;
import jadx.gui.ui.ContentPanel;
import jadx.gui.ui.TabbedPane;
import jadx.gui.utils.Utils;
class CodePanel extends ContentPanel {
public final class CodePanel extends ContentPanel {
private static final long serialVersionUID = 5310536092010045565L;
......@@ -17,7 +19,7 @@ class CodePanel extends ContentPanel {
private final CodeArea codeArea;
private final JScrollPane scrollPane;
CodePanel(TabbedPane panel, JNode jnode) {
public CodePanel(TabbedPane panel, JNode jnode) {
super(panel, jnode);
codeArea = new CodeArea(this);
......@@ -55,12 +57,12 @@ class CodePanel extends ContentPanel {
}
@Override
TabbedPane getTabbedPane() {
public TabbedPane getTabbedPane() {
return tabbedPane;
}
@Override
JNode getNode() {
public JNode getNode() {
return node;
}
......@@ -68,7 +70,7 @@ class CodePanel extends ContentPanel {
return searchBar;
}
CodeArea getCodeArea() {
public CodeArea getCodeArea() {
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.border.Border;
......
package jadx.gui.ui;
package jadx.gui.ui.codearea;
import javax.swing.*;
import javax.swing.text.BadLocationException;
......@@ -112,12 +112,7 @@ class SearchBar extends JToolBar {
JButton closeButton = new JButton();
closeButton.setIcon(ICON_CLOSE);
closeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
toggle();
}
});
closeButton.addActionListener(l -> toggle());
closeButton.setBorderPainted(false);
add(closeButton);
......
......@@ -5,10 +5,10 @@ import java.util.List;
public class JumpManager {
private List<Position> list = new ArrayList<>();
private List<JumpPosition> list = new ArrayList<>();
private int currentPos = 0;
public void addPosition(Position pos) {
public void addPosition(JumpPosition pos) {
if (pos.equals(getCurrent())) {
return;
}
......@@ -25,14 +25,14 @@ public class JumpManager {
}
}
private Position getCurrent() {
private JumpPosition getCurrent() {
if (currentPos >= 0 && currentPos < list.size()) {
return list.get(currentPos);
}
return null;
}
public Position getPrev() {
public JumpPosition getPrev() {
if (currentPos == 0) {
return null;
}
......@@ -40,7 +40,7 @@ public class JumpManager {
return list.get(currentPos);
}
public Position getNext() {
public JumpPosition getNext() {
int size = list.size();
if (size == 0) {
currentPos = 0;
......@@ -51,7 +51,7 @@ public class JumpManager {
currentPos = size - 1;
return null;
}
Position position = list.get(newPos);
JumpPosition position = list.get(newPos);
if (position == null) {
return null;
}
......
package jadx.gui.utils;
import jadx.api.CodePosition;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
public class Position {
public class JumpPosition {
private final JNode node;
private final int line;
public Position(CodePosition pos) {
this.node = new JClass(pos.getJavaClass());
this.line = pos.getLine();
}
public Position(JNode node, int line) {
public JumpPosition(JNode node, int line) {
this.node = node;
this.line = line;
}
......@@ -31,10 +24,10 @@ public class Position {
if (this == obj) {
return true;
}
if (!(obj instanceof Position)) {
if (!(obj instanceof JumpPosition)) {
return false;
}
Position position = (Position) obj;
JumpPosition position = (JumpPosition) obj;
return line == position.line && node.equals(position.node);
}
......
......@@ -114,6 +114,7 @@ popup.paste=Paste
popup.delete=Delete
popup.select_all=Select All
popup.find_usage=Find Usage
certificate.title=Certificate
certificate.cert_type=Type
certificate.serialSigVer=Version
......
package jadx.gui.tests
import jadx.gui.utils.JumpManager
import jadx.gui.utils.Position
import jadx.gui.utils.JumpPosition
import spock.lang.Specification
class TestJumpManager extends Specification {
......@@ -29,7 +29,7 @@ class TestJumpManager extends Specification {
def "1 element"() {
when:
jm.addPosition(Mock(Position))
jm.addPosition(Mock(JumpPosition))
then:
jm.getPrev() == null
jm.getNext() == null
......@@ -37,9 +37,9 @@ class TestJumpManager extends Specification {
def "2 elements"() {
when:
def mock1 = Mock(Position)
def mock1 = Mock(JumpPosition)
jm.addPosition(mock1)
def mock2 = Mock(Position)
def mock2 = Mock(JumpPosition)
jm.addPosition(mock2)
// 1 - 2@
then:
......@@ -52,15 +52,15 @@ class TestJumpManager extends Specification {
def "navigation"() {
expect:
def mock1 = Mock(Position)
def mock1 = Mock(JumpPosition)
jm.addPosition(mock1)
// 1@
def mock2 = Mock(Position)
def mock2 = Mock(JumpPosition)
jm.addPosition(mock2)
// 1 - 2@
jm.getPrev() == mock1
// 1@ - 2
def mock3 = Mock(Position)
def mock3 = Mock(JumpPosition)
jm.addPosition(mock3)
// 1 - 3@
jm.getNext() == null
......@@ -71,23 +71,23 @@ class TestJumpManager extends Specification {
def "navigation2"() {
expect:
def mock1 = Mock(Position)
def mock1 = Mock(JumpPosition)
jm.addPosition(mock1)
// 1@
def mock2 = Mock(Position)
def mock2 = Mock(JumpPosition)
jm.addPosition(mock2)
// 1 - 2@
def mock3 = Mock(Position)
def mock3 = Mock(JumpPosition)
jm.addPosition(mock3)
// 1 - 2 - 3@
def mock4 = Mock(Position)
def mock4 = Mock(JumpPosition)
jm.addPosition(mock4)
// 1 - 2 - 3 - 4@
jm.getPrev() == mock3
// 1 - 2 - 3@ - 4
jm.getPrev() == mock2
// 1 - 2@ - 3 - 4
def mock5 = Mock(Position)
def mock5 = Mock(JumpPosition)
jm.addPosition(mock5)
// 1 - 2 - 5@
jm.getNext() == null
......@@ -106,7 +106,7 @@ class TestJumpManager extends Specification {
def "add same element"() {
when:
def mock = Mock(Position)
def mock = Mock(JumpPosition)
jm.addPosition(mock)
jm.addPosition(mock)
then:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册