fix(gui): make link for full class names (#378)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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;
|
||||
|
||||
@@ -1,361 +0,0 @@
|
||||
package jadx.gui.ui;
|
||||
|
||||
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.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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.utils.Position;
|
||||
|
||||
public final class CodeArea extends RSyntaxTextArea {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
|
||||
|
||||
private static final long serialVersionUID = 6312736869579635796L;
|
||||
|
||||
private final CodePanel contentPanel;
|
||||
private final JNode node;
|
||||
|
||||
CodeArea(CodePanel panel) {
|
||||
this.contentPanel = panel;
|
||||
this.node = panel.getNode();
|
||||
|
||||
setMarkOccurrences(true);
|
||||
setEditable(false);
|
||||
loadSettings();
|
||||
|
||||
Caret caret = getCaret();
|
||||
if (caret instanceof DefaultCaret) {
|
||||
((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
|
||||
}
|
||||
caret.setVisible(true);
|
||||
|
||||
setSyntaxEditingStyle(node.getSyntaxName());
|
||||
if (node instanceof JClass) {
|
||||
setHyperlinksEnabled(true);
|
||||
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator((JClass) node);
|
||||
setLinkGenerator(codeLinkProcessor);
|
||||
addHyperlinkListener(codeLinkProcessor);
|
||||
addMenuItems(this, (JClass) node);
|
||||
}
|
||||
registerWordHighlighter();
|
||||
setText(node.getContent());
|
||||
}
|
||||
|
||||
private void registerWordHighlighter() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent evt) {
|
||||
if (evt.getClickCount() % 2 == 0 && !evt.isConsumed()) {
|
||||
evt.consume();
|
||||
String str = getSelectedText();
|
||||
if (str != null) {
|
||||
highlightAllMatches(str);
|
||||
}
|
||||
} else {
|
||||
highlightAllMatches(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param str - if null -> reset current highlights
|
||||
*/
|
||||
private void highlightAllMatches(@Nullable String str) {
|
||||
SearchContext context = new SearchContext(str);
|
||||
context.setMarkAll(true);
|
||||
context.setMatchCase(true);
|
||||
context.setWholeWord(true);
|
||||
SearchEngine.markAll(this, context);
|
||||
}
|
||||
|
||||
private void addMenuItems(CodeArea codeArea, JClass jCls) {
|
||||
Action findUsage = new FindUsageAction(codeArea, jCls);
|
||||
|
||||
JPopupMenu popup = getPopupMenu();
|
||||
popup.addSeparator();
|
||||
popup.add(findUsage);
|
||||
popup.addPopupMenuListener((PopupMenuListener) findUsage);
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
loadCommonSettings(contentPanel.getTabbedPane().getMainWindow(), this);
|
||||
}
|
||||
|
||||
public static void loadCommonSettings(MainWindow mainWindow, RSyntaxTextArea area) {
|
||||
area.setAntiAliasingEnabled(true);
|
||||
mainWindow.getEditorTheme().apply(area);
|
||||
|
||||
JadxSettings settings = mainWindow.getSettings();
|
||||
area.setFont(settings.getFont());
|
||||
}
|
||||
|
||||
public static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) {
|
||||
RSyntaxTextArea area = new RSyntaxTextArea();
|
||||
loadCommonSettings(mainWindow, area);
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
CodePosition pos = jCls.getCls().getDefinitionPosition(node);
|
||||
if (pos == null) {
|
||||
return null;
|
||||
}
|
||||
return new Position(pos);
|
||||
}
|
||||
|
||||
static JavaNode getJavaNodeAtOffset(JClass jCls, RSyntaxTextArea textArea, int offset) {
|
||||
try {
|
||||
int line = textArea.getLineOfOffset(offset);
|
||||
int lineOffset = offset - textArea.getLineStartOffset(line);
|
||||
return jCls.getCls().getJavaNodeAtPosition(line + 1, lineOffset + 1);
|
||||
} catch (BadLocationException e) {
|
||||
LOG.error("Can't get java node by offset", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Position getCurrentPosition() {
|
||||
return new Position(node, getCaretLineNumber() + 1);
|
||||
}
|
||||
|
||||
Integer getSourceLine(int line) {
|
||||
return node.getSourceLine(line);
|
||||
}
|
||||
|
||||
void scrollToLine(int line) {
|
||||
int lineNum = line - 1;
|
||||
if (lineNum < 0) {
|
||||
lineNum = 0;
|
||||
}
|
||||
setCaretAtLine(lineNum);
|
||||
centerCurrentLine();
|
||||
forceCurrentLineHighlightRepaint();
|
||||
}
|
||||
|
||||
public void centerCurrentLine() {
|
||||
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, this);
|
||||
if (viewport == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Rectangle r = modelToView(getCaretPosition());
|
||||
if (r == null) {
|
||||
return;
|
||||
}
|
||||
int extentHeight = viewport.getExtentSize().height;
|
||||
Dimension viewSize = viewport.getViewSize();
|
||||
if (viewSize == null) {
|
||||
return;
|
||||
}
|
||||
int viewHeight = viewSize.height;
|
||||
|
||||
int y = Math.max(0, r.y - extentHeight / 2);
|
||||
y = Math.min(y, viewHeight - extentHeight);
|
||||
|
||||
viewport.setViewPosition(new Point(0, y));
|
||||
} catch (BadLocationException e) {
|
||||
LOG.debug("Can't center current line", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCaretAtLine(int line) {
|
||||
try {
|
||||
setCaretPosition(getLineStartOffset(line));
|
||||
} catch (BadLocationException 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")
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import javax.swing.*;
|
||||
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.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rtextarea.SearchContext;
|
||||
import org.fife.ui.rtextarea.SearchEngine;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.CodePosition;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
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);
|
||||
|
||||
private static final long serialVersionUID = 6312736869579635796L;
|
||||
|
||||
private final CodePanel contentPanel;
|
||||
private final JNode node;
|
||||
|
||||
CodeArea(CodePanel panel) {
|
||||
this.contentPanel = panel;
|
||||
this.node = panel.getNode();
|
||||
|
||||
setMarkOccurrences(true);
|
||||
setEditable(false);
|
||||
loadSettings();
|
||||
|
||||
Caret caret = getCaret();
|
||||
if (caret instanceof DefaultCaret) {
|
||||
((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
|
||||
}
|
||||
caret.setVisible(true);
|
||||
|
||||
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(contentPanel, this, jClsNode);
|
||||
setLinkGenerator(codeLinkProcessor);
|
||||
addHyperlinkListener(codeLinkProcessor);
|
||||
addMenuItems(jClsNode);
|
||||
}
|
||||
registerWordHighlighter();
|
||||
setText(node.getContent());
|
||||
}
|
||||
|
||||
private void registerWordHighlighter() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent evt) {
|
||||
if (evt.getClickCount() % 2 == 0 && !evt.isConsumed()) {
|
||||
evt.consume();
|
||||
String str = getSelectedText();
|
||||
if (str != null) {
|
||||
highlightAllMatches(str);
|
||||
}
|
||||
} else {
|
||||
highlightAllMatches(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param str - if null -> reset current highlights
|
||||
*/
|
||||
private void highlightAllMatches(@Nullable String str) {
|
||||
SearchContext context = new SearchContext(str);
|
||||
context.setMarkAll(true);
|
||||
context.setMatchCase(true);
|
||||
context.setWholeWord(true);
|
||||
SearchEngine.markAll(this, context);
|
||||
}
|
||||
|
||||
private void addMenuItems(JClass jCls) {
|
||||
Action findUsage = new FindUsageAction(contentPanel, this, jCls);
|
||||
|
||||
JPopupMenu popup = getPopupMenu();
|
||||
popup.addSeparator();
|
||||
popup.add(findUsage);
|
||||
popup.addPopupMenuListener((PopupMenuListener) findUsage);
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
loadCommonSettings(contentPanel.getTabbedPane().getMainWindow(), this);
|
||||
}
|
||||
|
||||
public static void loadCommonSettings(MainWindow mainWindow, RSyntaxTextArea area) {
|
||||
area.setAntiAliasingEnabled(true);
|
||||
mainWindow.getEditorTheme().apply(area);
|
||||
|
||||
JadxSettings settings = mainWindow.getSettings();
|
||||
area.setFont(settings.getFont());
|
||||
}
|
||||
|
||||
public static RSyntaxTextArea getDefaultArea(MainWindow mainWindow) {
|
||||
RSyntaxTextArea area = new RSyntaxTextArea();
|
||||
loadCommonSettings(mainWindow, area);
|
||||
return area;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(foundNode);
|
||||
if (pos == null) {
|
||||
return null;
|
||||
}
|
||||
JNode jNode = contentPanel.getTabbedPane().getMainWindow().getCacheObject().getNodeCache().makeFrom(foundNode);
|
||||
return new JumpPosition(jNode.getRootClass(), pos.getLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search referenced java node by offset in {@code jCls} code
|
||||
*/
|
||||
public JavaNode getJavaNodeAtOffset(JClass jCls, int offset) {
|
||||
try {
|
||||
// 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 (Exception e) {
|
||||
LOG.error("Can't get java node by offset: {}", offset, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public JumpPosition getCurrentPosition() {
|
||||
return new JumpPosition(node, getCaretLineNumber() + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Integer getSourceLine(int line) {
|
||||
return node.getSourceLine(line);
|
||||
}
|
||||
|
||||
public void scrollToLine(int line) {
|
||||
int lineNum = line - 1;
|
||||
if (lineNum < 0) {
|
||||
lineNum = 0;
|
||||
}
|
||||
setCaretAtLine(lineNum);
|
||||
centerCurrentLine();
|
||||
forceCurrentLineHighlightRepaint();
|
||||
}
|
||||
|
||||
public void centerCurrentLine() {
|
||||
JViewport viewport = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, this);
|
||||
if (viewport == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Rectangle r = modelToView(getCaretPosition());
|
||||
if (r == null) {
|
||||
return;
|
||||
}
|
||||
int extentHeight = viewport.getExtentSize().height;
|
||||
Dimension viewSize = viewport.getViewSize();
|
||||
if (viewSize == null) {
|
||||
return;
|
||||
}
|
||||
int viewHeight = viewSize.height;
|
||||
|
||||
int y = Math.max(0, r.y - extentHeight / 2);
|
||||
y = Math.min(y, viewHeight - extentHeight);
|
||||
|
||||
viewport.setViewPosition(new Point(0, y));
|
||||
} catch (BadLocationException e) {
|
||||
LOG.debug("Can't center current line", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCaretAtLine(int line) {
|
||||
try {
|
||||
setCaretPosition(getLineStartOffset(line));
|
||||
} catch (BadLocationException e) {
|
||||
LOG.debug("Can't scroll to {}", line, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-6
@@ -1,4 +1,4 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package jadx.gui.ui;
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
+2
-7
@@ -1,4 +1,4 @@
|
||||
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;
|
||||
}
|
||||
|
||||
+4
-11
@@ -1,19 +1,12 @@
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user