Commit 5f8f454b authored by Skylot's avatar Skylot

gui: show resources

parent 3700ecb7
......@@ -4,6 +4,7 @@ import jadx.api.IJadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JavaClass;
import jadx.api.JavaPackage;
import jadx.api.ResourceFile;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
......@@ -42,7 +43,6 @@ public class JadxWrapper {
public void run() {
try {
decompiler.setOutputDir(dir);
decompiler.parseAndSaveXML();
ThreadPoolExecutor ex = (ThreadPoolExecutor) decompiler.getSaveExecutor();
ex.shutdown();
while (ex.isTerminating()) {
......@@ -69,6 +69,10 @@ public class JadxWrapper {
return decompiler.getPackages();
}
public List<ResourceFile> getResources() {
return decompiler.getResources();
}
public File getOpenFile() {
return openFile;
}
......
......@@ -10,6 +10,8 @@ import jadx.gui.utils.Utils;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
public class JClass extends JNode {
private static final long serialVersionUID = -1239986875244097177L;
......@@ -68,11 +70,16 @@ public class JClass extends JNode {
}
}
public String getCode() {
public String getContent() {
return cls.getCode();
}
@Override
public String getSyntaxName() {
return SyntaxConstants.SYNTAX_STYLE_JAVA;
}
@Override
public Icon getIcon() {
AccessInfo accessInfo = cls.getAccessInfo();
if (accessInfo.isEnum()) {
......@@ -119,6 +126,11 @@ public class JClass extends JNode {
}
@Override
public Integer getSourceLine(int line) {
return cls.getSourceLine(line);
}
@Override
public int hashCode() {
return cls.hashCode();
}
......
......@@ -9,6 +9,8 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
import javax.swing.Icon;
import javax.swing.tree.DefaultMutableTreeNode;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
public abstract class JNode extends DefaultMutableTreeNode {
public static JNode makeFrom(JavaNode node) {
if (node instanceof JavaClass) {
......@@ -38,7 +40,21 @@ public abstract class JNode extends DefaultMutableTreeNode {
return null;
}
public abstract int getLine();
public String getContent() {
return null;
}
public String getSyntaxName() {
return SyntaxConstants.SYNTAX_STYLE_NONE;
}
public int getLine() {
return 0;
}
public Integer getSourceLine(int line) {
return null;
}
public abstract Icon getIcon();
......
package jadx.gui.treemodel;
import jadx.api.ResourceFile;
import jadx.api.ResourceType;
import jadx.core.codegen.CodeWriter;
import jadx.gui.utils.OverlayIcon;
import jadx.gui.utils.Utils;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
public class JResource extends JNode implements Comparable<JResource> {
private static final long serialVersionUID = -201018424302612434L;
private static final ImageIcon ROOT_ICON = Utils.openIcon("cf_obj");
private static final ImageIcon FOLDER_ICON = Utils.openIcon("folder");
private static final ImageIcon FILE_ICON = Utils.openIcon("file_obj");
private static final ImageIcon MANIFEST_ICON = Utils.openIcon("template_obj");
private static final ImageIcon JAVA_ICON = Utils.openIcon("java_ovr");
private static final ImageIcon ERROR_ICON = Utils.openIcon("error_co");
public static enum JResType {
ROOT,
DIR,
FILE
}
private final String name;
private final List<JResource> files = new ArrayList<JResource>(1);
private final JResType type;
private final ResourceFile resFile;
private boolean loaded;
private String content;
private Map<Integer, Integer> lineMapping;
public JResource(ResourceFile resFile, String name, JResType type) {
this.resFile = resFile;
this.name = name;
this.type = type;
}
public final void update() {
removeAllChildren();
for (JResource res : files) {
res.update();
add(res);
}
}
public String getName() {
return name;
}
public List<JResource> getFiles() {
return files;
}
public String getContent() {
if (!loaded && resFile != null && type == JResType.FILE) {
loaded = true;
if (isSupportedForView(resFile.getType())) {
CodeWriter cw = resFile.getContent();
if (cw != null) {
lineMapping = cw.getLineMapping();
content = cw.toString();
}
}
}
return content;
}
@Override
public Integer getSourceLine(int line) {
if (lineMapping == null) {
return null;
}
return lineMapping.get(line);
}
@Override
public String getSyntaxName() {
switch (resFile.getType()) {
case CODE:
return super.getSyntaxName();
case MANIFEST:
case XML:
return SyntaxConstants.SYNTAX_STYLE_XML;
}
String syntax = getSyntaxByExtension(resFile.getName());
if (syntax != null) {
return syntax;
}
return super.getSyntaxName();
}
private String getSyntaxByExtension(String name) {
int dot = name.lastIndexOf('.');
if (dot == -1) {
return null;
}
String ext = name.substring(dot + 1);
if (ext.equals("js")) {
return SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT;
}
if (ext.equals("css")) {
return SyntaxConstants.SYNTAX_STYLE_CSS;
}
if (ext.equals("html")) {
return SyntaxConstants.SYNTAX_STYLE_HTML;
}
return null;
}
@Override
public Icon getIcon() {
switch (type) {
case ROOT:
return ROOT_ICON;
case DIR:
return FOLDER_ICON;
case FILE:
ResourceType resType = resFile.getType();
if (resType == ResourceType.MANIFEST) {
return MANIFEST_ICON;
}
if (resType == ResourceType.CODE) {
return new OverlayIcon(FILE_ICON, ERROR_ICON, JAVA_ICON);
}
if (!isSupportedForView(resType)) {
return new OverlayIcon(FILE_ICON, ERROR_ICON);
}
return FILE_ICON;
}
return FILE_ICON;
}
private boolean isSupportedForView(ResourceType type) {
switch (type) {
case CODE:
case XML:
case ARSC:
case FONT:
case IMG:
case LIB:
return false;
case MANIFEST:
case UNKNOWN:
return true;
}
return true;
}
@Override
public JClass getJParent() {
return null;
}
@Override
public int compareTo(JResource o) {
return name.compareTo(o.name);
}
@Override
public String makeString() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
return name.equals(((JResource) o).name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
package jadx.gui.treemodel;
import jadx.api.JavaPackage;
import jadx.api.ResourceFile;
import jadx.gui.JadxWrapper;
import jadx.gui.treemodel.JResource.JResType;
import jadx.gui.utils.Utils;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
public class JRoot extends JNode {
private static final long serialVersionUID = 8888495789773527342L;
......@@ -33,102 +29,53 @@ public class JRoot extends JNode {
public final void update() {
removeAllChildren();
if (flatPackages) {
for (JavaPackage pkg : wrapper.getPackages()) {
add(new JPackage(pkg));
}
} else {
// build packages hierarchy
List<JPackage> rootPkgs = getHierarchyPackages(wrapper.getPackages());
for (JPackage jPackage : rootPkgs) {
jPackage.update();
add(jPackage);
}
add(new JSources(this, wrapper));
List<JResource> resList = getHierarchyResources(wrapper.getResources());
for (JResource jRes : resList) {
jRes.update();
add(jRes);
}
}
/**
* Convert packages list to hierarchical packages representation
*
* @param packages input packages list
* @return root packages
*/
List<JPackage> getHierarchyPackages(List<JavaPackage> packages) {
Map<String, JPackage> pkgMap = new HashMap<String, JPackage>();
for (JavaPackage pkg : packages) {
addPackage(pkgMap, new JPackage(pkg));
private List<JResource> getHierarchyResources(List<ResourceFile> resources) {
if (resources.isEmpty()) {
return Collections.emptyList();
}
// merge packages without classes
boolean repeat;
do {
repeat = false;
for (JPackage pkg : pkgMap.values()) {
if (pkg.getInnerPackages().size() == 1 && pkg.getClasses().isEmpty()) {
JPackage innerPkg = pkg.getInnerPackages().get(0);
pkg.getInnerPackages().clear();
pkg.getInnerPackages().addAll(innerPkg.getInnerPackages());
pkg.getClasses().addAll(innerPkg.getClasses());
pkg.setName(pkg.getName() + "." + innerPkg.getName());
innerPkg.getInnerPackages().clear();
innerPkg.getClasses().clear();
repeat = true;
break;
JResource root = new JResource(null, "Resources", JResType.ROOT);
String splitPathStr = Pattern.quote(File.separator);
for (ResourceFile rf : resources) {
String[] parts = new File(rf.getName()).getPath().split(splitPathStr);
JResource curRf = root;
int count = parts.length;
for (int i = 0; i < count; i++) {
String name = parts[i];
JResource subRF = getResourceByName(curRf, name);
if (subRF == null) {
subRF = new JResource(rf, name, i != count - 1 ? JResType.DIR : JResType.FILE);
curRf.getFiles().add(subRF);
}
}
} while (repeat);
// remove empty packages
for (Iterator<Map.Entry<String, JPackage>> it = pkgMap.entrySet().iterator(); it.hasNext(); ) {
JPackage pkg = it.next().getValue();
if (pkg.getInnerPackages().isEmpty() && pkg.getClasses().isEmpty()) {
it.remove();
curRf = subRF;
}
}
// use identity set for collect inner packages
Set<JPackage> innerPackages = Collections.newSetFromMap(new IdentityHashMap<JPackage, Boolean>());
for (JPackage pkg : pkgMap.values()) {
innerPackages.addAll(pkg.getInnerPackages());
}
// find root packages
List<JPackage> rootPkgs = new ArrayList<JPackage>();
for (JPackage pkg : pkgMap.values()) {
if (!innerPackages.contains(pkg)) {
rootPkgs.add(pkg);
}
}
Collections.sort(rootPkgs);
return rootPkgs;
return Collections.singletonList(root);
}
private void addPackage(Map<String, JPackage> pkgs, JPackage pkg) {
String pkgName = pkg.getName();
JPackage replaced = pkgs.put(pkgName, pkg);
if (replaced != null) {
pkg.getInnerPackages().addAll(replaced.getInnerPackages());
pkg.getClasses().addAll(replaced.getClasses());
}
int dot = pkgName.lastIndexOf('.');
if (dot > 0) {
String prevPart = pkgName.substring(0, dot);
String shortName = pkgName.substring(dot + 1);
pkg.setName(shortName);
JPackage prevPkg = pkgs.get(prevPart);
if (prevPkg == null) {
prevPkg = new JPackage(prevPart);
addPackage(pkgs, prevPkg);
private JResource getResourceByName(JResource rf, String name) {
for (JResource sub : rf.getFiles()) {
if (sub.getName().equals(name)) {
return sub;
}
prevPkg.getInnerPackages().add(pkg);
}
return null;
}
public JClass searchClassInTree(JClass node) {
public JNode searchClassInTree(JNode node) {
Enumeration en = this.breadthFirstEnumeration();
while (en.hasMoreElements()) {
Object obj = en.nextElement();
if (node.equals(obj)) {
return (JClass) obj;
return (JNode) obj;
}
}
return null;
......
package jadx.gui.treemodel;
import jadx.api.JavaPackage;
import jadx.gui.JadxWrapper;
import jadx.gui.utils.Utils;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JSources extends JNode {
private static final long serialVersionUID = 8962924556824862801L;
private static final ImageIcon ROOT_ICON = Utils.openIcon("packagefolder_obj");
private final JadxWrapper wrapper;
private final boolean flatPackages;
public JSources(JRoot jRoot, JadxWrapper wrapper) {
this.flatPackages = jRoot.isFlatPackages();
this.wrapper = wrapper;
update();
}
public final void update() {
removeAllChildren();
if (flatPackages) {
for (JavaPackage pkg : wrapper.getPackages()) {
add(new JPackage(pkg));
}
} else {
// build packages hierarchy
List<JPackage> rootPkgs = getHierarchyPackages(wrapper.getPackages());
for (JPackage jPackage : rootPkgs) {
jPackage.update();
add(jPackage);
}
}
}
/**
* Convert packages list to hierarchical packages representation
*
* @param packages input packages list
* @return root packages
*/
List<JPackage> getHierarchyPackages(List<JavaPackage> packages) {
Map<String, JPackage> pkgMap = new HashMap<String, JPackage>();
for (JavaPackage pkg : packages) {
addPackage(pkgMap, new JPackage(pkg));
}
// merge packages without classes
boolean repeat;
do {
repeat = false;
for (JPackage pkg : pkgMap.values()) {
if (pkg.getInnerPackages().size() == 1 && pkg.getClasses().isEmpty()) {
JPackage innerPkg = pkg.getInnerPackages().get(0);
pkg.getInnerPackages().clear();
pkg.getInnerPackages().addAll(innerPkg.getInnerPackages());
pkg.getClasses().addAll(innerPkg.getClasses());
pkg.setName(pkg.getName() + "." + innerPkg.getName());
innerPkg.getInnerPackages().clear();
innerPkg.getClasses().clear();
repeat = true;
break;
}
}
} while (repeat);
// remove empty packages
for (Iterator<Map.Entry<String, JPackage>> it = pkgMap.entrySet().iterator(); it.hasNext(); ) {
JPackage pkg = it.next().getValue();
if (pkg.getInnerPackages().isEmpty() && pkg.getClasses().isEmpty()) {
it.remove();
}
}
// use identity set for collect inner packages
Set<JPackage> innerPackages = Collections.newSetFromMap(new IdentityHashMap<JPackage, Boolean>());
for (JPackage pkg : pkgMap.values()) {
innerPackages.addAll(pkg.getInnerPackages());
}
// find root packages
List<JPackage> rootPkgs = new ArrayList<JPackage>();
for (JPackage pkg : pkgMap.values()) {
if (!innerPackages.contains(pkg)) {
rootPkgs.add(pkg);
}
}
Collections.sort(rootPkgs);
return rootPkgs;
}
private void addPackage(Map<String, JPackage> pkgs, JPackage pkg) {
String pkgName = pkg.getName();
JPackage replaced = pkgs.put(pkgName, pkg);
if (replaced != null) {
pkg.getInnerPackages().addAll(replaced.getInnerPackages());
pkg.getClasses().addAll(replaced.getClasses());
}
int dot = pkgName.lastIndexOf('.');
if (dot > 0) {
String prevPart = pkgName.substring(0, dot);
String shortName = pkgName.substring(dot + 1);
pkg.setName(shortName);
JPackage prevPkg = pkgs.get(prevPart);
if (prevPkg == null) {
prevPkg = new JPackage(prevPart);
addPackage(pkgs, prevPkg);
}
prevPkg.getInnerPackages().add(pkg);
}
}
@Override
public Icon getIcon() {
return ROOT_ICON;
}
@Override
public JClass getJParent() {
return null;
}
@Override
public String makeString() {
return "Source code";
}
}
......@@ -2,6 +2,7 @@ package jadx.gui.ui;
import jadx.api.CodePosition;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.Position;
import javax.swing.JViewport;
......@@ -18,31 +19,26 @@ import java.awt.Rectangle;
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.SyntaxScheme;
import org.fife.ui.rsyntaxtextarea.Token;
import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class CodeArea extends RSyntaxTextArea {
private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
class ContentArea extends RSyntaxTextArea {
private static final Logger LOG = LoggerFactory.getLogger(ContentArea.class);
private static final long serialVersionUID = 6312736869579635796L;
public static final Color BACKGROUND = new Color(0xFAFAFA);
public static final Color JUMP_TOKEN_FGD = new Color(0x491BA1);
private final CodePanel codePanel;
private final JClass cls;
private final ContentPanel contentPanel;
private final JNode node;
CodeArea(CodePanel panel) {
this.codePanel = panel;
this.cls = panel.getCls();
setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
SyntaxScheme scheme = getSyntaxScheme();
scheme.getStyle(Token.FUNCTION).foreground = Color.BLACK;
ContentArea(ContentPanel panel) {
this.contentPanel = panel;
this.node = panel.getNode();
setMarkOccurrences(true);
setBackground(BACKGROUND);
......@@ -54,12 +50,19 @@ class CodeArea extends RSyntaxTextArea {
}
caret.setVisible(true);
setHyperlinksEnabled(true);
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator(cls);
setLinkGenerator(codeLinkProcessor);
addHyperlinkListener(codeLinkProcessor);
setSyntaxEditingStyle(node.getSyntaxName());
if (node instanceof JClass) {
SyntaxScheme scheme = getSyntaxScheme();
scheme.getStyle(Token.FUNCTION).foreground = Color.BLACK;
setText(cls.getCode());
setHyperlinksEnabled(true);
CodeLinkGenerator codeLinkProcessor = new CodeLinkGenerator((JClass) node);
setLinkGenerator(codeLinkProcessor);
addHyperlinkListener(codeLinkProcessor);
}
setText(node.getContent());
}
private boolean isJumpToken(Token token) {
......@@ -71,9 +74,11 @@ class CodeArea extends RSyntaxTextArea {
return false;
}
}
Position pos = getPosition(cls, this, token.getOffset());
if (pos != null) {
return true;
if (node instanceof JClass) {
Position pos = getPosition((JClass) node, this, token.getOffset());
if (pos != null) {
return true;
}
}
}
return false;
......@@ -102,11 +107,11 @@ class CodeArea extends RSyntaxTextArea {
}
Position getCurrentPosition() {
return new Position(cls, getCaretLineNumber() + 1);
return new Position(node, getCaretLineNumber() + 1);
}
Integer getSourceLine(int line) {
return cls.getCls().getSourceLine(line);
return node.getSourceLine(line);
}
void scrollToLine(int line) {
......@@ -172,7 +177,7 @@ class CodeArea extends RSyntaxTextArea {
@Override
public HyperlinkEvent execute() {
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
defPos.getCls().getFullName());
defPos.getNode().makeLongString());
}
@Override
......@@ -192,7 +197,7 @@ class CodeArea extends RSyntaxTextArea {
if (obj instanceof Position) {
Position pos = (Position) obj;
LOG.debug("Code jump to: {}", pos);
TabbedPane tabbedPane = codePanel.getTabbedPane();
TabbedPane tabbedPane = contentPanel.getTabbedPane();
tabbedPane.getJumpManager().addPosition(getCurrentPosition());
tabbedPane.getJumpManager().addPosition(pos);
tabbedPane.showCode(pos);
......
package jadx.gui.ui;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.Utils;
import javax.swing.AbstractAction;
......@@ -12,31 +12,31 @@ import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
class CodePanel extends JPanel {
class ContentPanel extends JPanel {
private static final long serialVersionUID = 5310536092010045565L;
private final TabbedPane tabbedPane;
private final JClass jClass;
private final JNode node;
private final SearchBar searchBar;
private final CodeArea codeArea;
private final ContentArea contentArea;
private final JScrollPane scrollPane;
CodePanel(TabbedPane panel, JClass cls) {
ContentPanel(TabbedPane panel, JNode node) {
tabbedPane = panel;
jClass = cls;
codeArea = new CodeArea(this);
searchBar = new SearchBar(codeArea);
this.node = node;
contentArea = new ContentArea(this);
searchBar = new SearchBar(contentArea);
scrollPane = new JScrollPane(codeArea);
scrollPane.setRowHeaderView(new LineNumbers(codeArea));
scrollPane = new JScrollPane(contentArea);
scrollPane.setRowHeaderView(new LineNumbers(contentArea));
setLayout(new BorderLayout());
add(searchBar, BorderLayout.NORTH);
add(scrollPane);
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_MASK);
Utils.addKeyBinding(codeArea, key, "SearchAction", new SearchAction());
Utils.addKeyBinding(contentArea, key, "SearchAction", new SearchAction());
}
private class SearchAction extends AbstractAction {
......@@ -52,16 +52,16 @@ class CodePanel extends JPanel {
return tabbedPane;
}
JClass getCls() {
return jClass;
JNode getNode() {
return node;
}
SearchBar getSearchBar() {
return searchBar;
}
CodeArea getCodeArea() {
return codeArea;
ContentArea getContentArea() {
return contentArea;
}
JScrollPane getScrollPane() {
......
......@@ -32,18 +32,18 @@ public class LineNumbers extends JPanel implements CaretListener {
private static final int HEIGHT = Integer.MAX_VALUE - 1000000;
private static final Color FOREGROUND = Color.GRAY;
private static final Color BACKGROUND = CodeArea.BACKGROUND;
private static final Color BACKGROUND = ContentArea.BACKGROUND;
private static final Color CURRENT_LINE_FOREGROUND = new Color(227, 0, 0);
private CodeArea codeArea;
private ContentArea contentArea;
private boolean useSourceLines = true;
private int lastDigits;
private int lastLine;
private Map<String, FontMetrics> fonts;
public LineNumbers(CodeArea component) {
this.codeArea = component;
public LineNumbers(ContentArea component) {
this.contentArea = component;
setFont(component.getFont());
setBackground(BACKGROUND);
setForeground(FOREGROUND);
......@@ -70,7 +70,7 @@ public class LineNumbers extends JPanel implements CaretListener {
}
private void setPreferredWidth() {
Element root = codeArea.getDocument().getDefaultRootElement();
Element root = contentArea.getDocument().getDefaultRootElement();
int lines = root.getElementCount();
int digits = Math.max(String.valueOf(lines).length(), 3);
if (lastDigits != digits) {
......@@ -92,12 +92,12 @@ public class LineNumbers extends JPanel implements CaretListener {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
FontMetrics fontMetrics = codeArea.getFontMetrics(codeArea.getFont());
FontMetrics fontMetrics = contentArea.getFontMetrics(contentArea.getFont());
Insets insets = getInsets();
int availableWidth = getSize().width - insets.left - insets.right;
Rectangle clip = g.getClipBounds();
int rowStartOffset = codeArea.viewToModel(new Point(0, clip.y));
int endOffset = codeArea.viewToModel(new Point(0, clip.y + clip.height));
int rowStartOffset = contentArea.viewToModel(new Point(0, clip.y));
int endOffset = contentArea.viewToModel(new Point(0, clip.y + clip.height));
while (rowStartOffset <= endOffset) {
try {
......@@ -111,7 +111,7 @@ public class LineNumbers extends JPanel implements CaretListener {
int x = availableWidth - stringWidth + insets.left;
int y = getOffsetY(rowStartOffset, fontMetrics);
g.drawString(lineNumber, x, y);
rowStartOffset = Utilities.getRowEnd(codeArea, rowStartOffset) + 1;
rowStartOffset = Utilities.getRowEnd(contentArea, rowStartOffset) + 1;
} catch (Exception e) {
break;
}
......@@ -119,19 +119,19 @@ public class LineNumbers extends JPanel implements CaretListener {
}
private boolean isCurrentLine(int rowStartOffset) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
int caretPosition = contentArea.getCaretPosition();
Element root = contentArea.getDocument().getDefaultRootElement();
return root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition);
}
protected String getTextLineNumber(int rowStartOffset) {
Element root = codeArea.getDocument().getDefaultRootElement();
Element root = contentArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
if (line.getStartOffset() == rowStartOffset) {
int lineNumber = index + 1;
if (useSourceLines) {
Integer sourceLine = codeArea.getSourceLine(lineNumber);
Integer sourceLine = contentArea.getSourceLine(lineNumber);
if (sourceLine != null) {
return String.valueOf(sourceLine);
}
......@@ -143,7 +143,7 @@ public class LineNumbers extends JPanel implements CaretListener {
}
private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics) throws BadLocationException {
Rectangle r = codeArea.modelToView(rowStartOffset);
Rectangle r = contentArea.modelToView(rowStartOffset);
if (r == null) {
throw new BadLocationException("Can't get Y offset", rowStartOffset);
}
......@@ -156,7 +156,7 @@ public class LineNumbers extends JPanel implements CaretListener {
if (fonts == null) {
fonts = new HashMap<String, FontMetrics>();
}
Element root = codeArea.getDocument().getDefaultRootElement();
Element root = contentArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
for (int i = 0; i < line.getElementCount(); i++) {
......@@ -168,7 +168,7 @@ public class LineNumbers extends JPanel implements CaretListener {
FontMetrics fm = fonts.get(key);
if (fm == null) {
Font font = new Font(fontFamily, Font.PLAIN, fontSize);
fm = codeArea.getFontMetrics(font);
fm = contentArea.getFontMetrics(font);
fonts.put(key, fm);
}
descent = Math.max(descent, fm.getDescent());
......@@ -179,8 +179,8 @@ public class LineNumbers extends JPanel implements CaretListener {
@Override
public void caretUpdate(CaretEvent e) {
int caretPosition = codeArea.getCaretPosition();
Element root = codeArea.getDocument().getDefaultRootElement();
int caretPosition = contentArea.getCaretPosition();
Element root = contentArea.getDocument().getDefaultRootElement();
int currentLine = root.getElementIndex(caretPosition);
if (lastLine != currentLine) {
repaint();
......
......@@ -3,6 +3,7 @@ package jadx.gui.ui;
import jadx.gui.JadxWrapper;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
import jadx.gui.treemodel.JRoot;
import jadx.gui.update.JadxUpdate;
import jadx.gui.update.JadxUpdate.IUpdateCallback;
......@@ -159,8 +160,12 @@ public class MainWindow extends JFrame {
treeRoot = new JRoot(wrapper);
treeRoot.setFlatPackages(isFlattenPackage);
treeModel.setRoot(treeRoot);
reloadTree();
}
private void reloadTree() {
treeModel.reload();
tree.expandRow(0);
tree.expandRow(1);
}
private void toggleFlattenPackage() {
......@@ -178,37 +183,46 @@ public class MainWindow extends JFrame {
if (root instanceof JRoot) {
JRoot treeRoot = (JRoot) root;
treeRoot.setFlatPackages(isFlattenPackage);
treeModel.reload();
tree.expandRow(0);
reloadTree();
}
}
private void treeClickAction() {
Object obj = tree.getLastSelectedPathComponent();
if (obj instanceof JNode) {
JNode node = (JNode) obj;
JClass cls = node.getRootClass();
if (cls != null) {
tabbedPane.showCode(new Position(cls, node.getLine()));
try {
Object obj = tree.getLastSelectedPathComponent();
if (obj instanceof JResource) {
JResource res = (JResource) obj;
if (res.getContent() != null) {
tabbedPane.showCode(new Position(res, res.getLine()));
}
}
if (obj instanceof JNode) {
JNode node = (JNode) obj;
JClass cls = node.getRootClass();
if (cls != null) {
tabbedPane.showCode(new Position(cls, node.getLine()));
}
}
} catch (Exception e) {
LOG.error("Content loading error", e);
}
}
private void syncWithEditor() {
CodePanel selectedCodePanel = tabbedPane.getSelectedCodePanel();
if (selectedCodePanel == null) {
ContentPanel selectedContentPanel = tabbedPane.getSelectedCodePanel();
if (selectedContentPanel == null) {
return;
}
JClass jCls = selectedCodePanel.getCls();
if (jCls.getParent() == null && treeRoot != null) {
JNode node = selectedContentPanel.getNode();
if (node.getParent() == null && treeRoot != null) {
// node not register in tree
jCls = treeRoot.searchClassInTree(jCls);
if (jCls == null) {
node = treeRoot.searchClassInTree(node);
if (node == null) {
LOG.error("Class not found in tree");
return;
}
}
TreeNode[] pathNodes = treeModel.getPathToRoot(jCls);
TreeNode[] pathNodes = treeModel.getPathToRoot(node);
if (pathNodes == null) {
return;
}
......@@ -218,9 +232,9 @@ public class MainWindow extends JFrame {
}
private void toggleFind() {
CodePanel codePanel = tabbedPane.getSelectedCodePanel();
if (codePanel != null) {
codePanel.getSearchBar().toggle();
ContentPanel contentPanel = tabbedPane.getSelectedCodePanel();
if (contentPanel != null) {
contentPanel.getSearchBar().toggle();
}
}
......@@ -432,8 +446,8 @@ public class MainWindow extends JFrame {
tree.setCellRenderer(new DefaultTreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(JTree tree,
Object value, boolean selected, boolean expanded,
boolean isLeaf, int row, boolean focused) {
Object value, boolean selected, boolean expanded,
boolean isLeaf, int row, boolean focused) {
Component c = super.getTreeCellRendererComponent(tree, value, selected, expanded, isLeaf, row, focused);
if (value instanceof JNode) {
setIcon(((JNode) value).getIcon());
......
package jadx.gui.ui;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.JumpManager;
import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
......@@ -37,7 +37,7 @@ class TabbedPane extends JTabbedPane {
private static final ImageIcon ICON_CLOSE_INACTIVE = Utils.openIcon("cross_grayed");
private final MainWindow mainWindow;
private final Map<JClass, CodePanel> openTabs = new LinkedHashMap<JClass, CodePanel>();
private final Map<JNode, ContentPanel> openTabs = new LinkedHashMap<JNode, ContentPanel>();
private JumpManager jumps = new JumpManager();
TabbedPane(MainWindow window) {
......@@ -66,14 +66,14 @@ class TabbedPane extends JTabbedPane {
}
void showCode(final Position pos) {
final CodePanel codePanel = getCodePanel(pos.getCls());
final ContentPanel contentPanel = getCodePanel(pos.getNode());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setSelectedComponent(codePanel);
CodeArea codeArea = codePanel.getCodeArea();
codeArea.scrollToLine(pos.getLine());
codeArea.requestFocus();
setSelectedComponent(contentPanel);
ContentArea contentArea = contentPanel.getContentArea();
contentArea.scrollToLine(pos.getLine());
contentArea.requestFocus();
}
});
}
......@@ -96,40 +96,40 @@ class TabbedPane extends JTabbedPane {
return jumps;
}
private void addCodePanel(CodePanel codePanel) {
openTabs.put(codePanel.getCls(), codePanel);
add(codePanel);
private void addCodePanel(ContentPanel contentPanel) {
openTabs.put(contentPanel.getNode(), contentPanel);
add(contentPanel);
}
private void closeCodePanel(CodePanel codePanel) {
openTabs.remove(codePanel.getCls());
remove(codePanel);
private void closeCodePanel(ContentPanel contentPanel) {
openTabs.remove(contentPanel.getNode());
remove(contentPanel);
}
private CodePanel getCodePanel(JClass cls) {
CodePanel panel = openTabs.get(cls);
private ContentPanel getCodePanel(JNode cls) {
ContentPanel panel = openTabs.get(cls);
if (panel == null) {
panel = new CodePanel(this, cls);
panel = new ContentPanel(this, cls);
addCodePanel(panel);
setTabComponentAt(indexOfComponent(panel), makeTabComponent(panel));
}
return panel;
}
CodePanel getSelectedCodePanel() {
return (CodePanel) getSelectedComponent();
ContentPanel getSelectedCodePanel() {
return (ContentPanel) getSelectedComponent();
}
private Component makeTabComponent(final CodePanel codePanel) {
JClass cls = codePanel.getCls();
String name = cls.getCls().getFullName();
private Component makeTabComponent(final ContentPanel contentPanel) {
JNode node = contentPanel.getNode();
String name = node.makeLongString();
final JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 3, 0));
panel.setOpaque(false);
final JLabel label = new JLabel(name);
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
label.setIcon(cls.getIcon());
label.setIcon(node.getIcon());
final JButton button = new JButton();
button.setIcon(ICON_CLOSE_INACTIVE);
......@@ -144,7 +144,7 @@ class TabbedPane extends JTabbedPane {
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
closeCodePanel(codePanel);
closeCodePanel(contentPanel);
}
});
......@@ -152,13 +152,13 @@ class TabbedPane extends JTabbedPane {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isMiddleMouseButton(e)) {
closeCodePanel(codePanel);
closeCodePanel(contentPanel);
} else if (SwingUtilities.isRightMouseButton(e)) {
JPopupMenu menu = createTabPopupMenu(codePanel);
JPopupMenu menu = createTabPopupMenu(contentPanel);
menu.show(panel, e.getX(), e.getY());
} else {
// TODO: make correct event delegation to tabbed pane
setSelectedComponent(codePanel);
setSelectedComponent(contentPanel);
}
}
});
......@@ -169,14 +169,14 @@ class TabbedPane extends JTabbedPane {
return panel;
}
private JPopupMenu createTabPopupMenu(final CodePanel codePanel) {
private JPopupMenu createTabPopupMenu(final ContentPanel contentPanel) {
JPopupMenu menu = new JPopupMenu();
JMenuItem closeTab = new JMenuItem(NLS.str("tabs.close"));
closeTab.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
closeCodePanel(codePanel);
closeCodePanel(contentPanel);
}
});
menu.add(closeTab);
......@@ -186,9 +186,9 @@ class TabbedPane extends JTabbedPane {
closeOther.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
List<CodePanel> codePanels = new ArrayList<CodePanel>(openTabs.values());
for (CodePanel panel : codePanels) {
if (panel != codePanel) {
List<ContentPanel> contentPanels = new ArrayList<ContentPanel>(openTabs.values());
for (ContentPanel panel : contentPanels) {
if (panel != contentPanel) {
closeCodePanel(panel);
}
}
......@@ -200,8 +200,8 @@ class TabbedPane extends JTabbedPane {
closeAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
List<CodePanel> codePanels = new ArrayList<CodePanel>(openTabs.values());
for (CodePanel panel : codePanels) {
List<ContentPanel> contentPanels = new ArrayList<ContentPanel>(openTabs.values());
for (ContentPanel panel : contentPanels) {
closeCodePanel(panel);
}
}
......@@ -209,14 +209,14 @@ class TabbedPane extends JTabbedPane {
menu.add(closeAll);
menu.addSeparator();
CodePanel selectedCodePanel = getSelectedCodePanel();
for (final Map.Entry<JClass, CodePanel> entry : openTabs.entrySet()) {
final CodePanel cp = entry.getValue();
if (cp == selectedCodePanel) {
ContentPanel selectedContentPanel = getSelectedCodePanel();
for (final Map.Entry<JNode, ContentPanel> entry : openTabs.entrySet()) {
final ContentPanel cp = entry.getValue();
if (cp == selectedContentPanel) {
continue;
}
JClass jClass = entry.getKey();
final String clsName = jClass.getCls().getFullName();
JNode node = entry.getKey();
final String clsName = node.makeLongString();
JMenuItem item = new JMenuItem(clsName);
item.addActionListener(new ActionListener() {
@Override
......@@ -224,7 +224,7 @@ class TabbedPane extends JTabbedPane {
setSelectedComponent(cp);
}
});
item.setIcon(jClass.getIcon());
item.setIcon(node.getIcon());
menu.add(item);
}
}
......
......@@ -18,6 +18,8 @@ import org.slf4j.LoggerFactory;
import static java.awt.Desktop.Action;
public class Link extends JLabel implements MouseListener {
private static final long serialVersionUID = 3655322136444908178L;
private static final Logger LOG = LoggerFactory.getLogger(JLabel.class);
private String url;
......
......@@ -4,6 +4,7 @@ import javax.swing.Icon;
import java.awt.Component;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class OverlayIcon implements Icon {
......@@ -19,6 +20,11 @@ public class OverlayIcon implements Icon {
this.icon = icon;
}
public OverlayIcon(Icon icon, Icon... ovrIcons) {
this.icon = icon;
Collections.addAll(icons, ovrIcons);
}
@Override
public int getIconHeight() {
return icon.getIconHeight();
......
......@@ -2,23 +2,24 @@ package jadx.gui.utils;
import jadx.api.CodePosition;
import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode;
public class Position {
private final JClass cls;
private final JNode node;
private final int line;
public Position(CodePosition pos) {
this.cls = new JClass(pos.getJavaClass());
this.node = new JClass(pos.getJavaClass());
this.line = pos.getLine();
}
public Position(JClass cls, int line) {
this.cls = cls;
public Position(JNode node, int line) {
this.node = node;
this.line = line;
}
public JClass getCls() {
return cls;
public JNode getNode() {
return node;
}
public int getLine() {
......@@ -34,16 +35,16 @@ public class Position {
return false;
}
Position position = (Position) obj;
return line == position.line && cls.equals(position.cls);
return line == position.line && node.equals(position.node);
}
@Override
public int hashCode() {
return 31 * cls.hashCode() + line;
return 31 * node.hashCode() + line;
}
@Override
public String toString() {
return "Position: " + cls + " : " + line;
return "Position: " + node + " : " + line;
}
}
......@@ -16,15 +16,19 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class JRootTest {
public class JSourcesTest {
private JRoot root;
private JSources sources;
private JadxDecompiler decompiler;
@Before
public void init() {
root = new JRoot(mock(JadxWrapper.class));
JRoot root = mock(JRoot.class);
when(root.isFlatPackages()).thenReturn(false);
JadxWrapper wrapper = mock(JadxWrapper.class);
sources = new JSources(root, wrapper);
decompiler = new JadxDecompiler(mock(IJadxArgs.class));
}
......@@ -33,7 +37,7 @@ public class JRootTest {
String pkgName = "a.b.c.d.e";
List<JavaPackage> packages = Arrays.asList(newPkg(pkgName));
List<JPackage> out = root.getHierarchyPackages(packages);
List<JPackage> out = sources.getHierarchyPackages(packages);
assertEquals(out.size(), 1);
JPackage jpkg = out.get(0);
......@@ -48,7 +52,7 @@ public class JRootTest {
newPkg("a.c"),
newPkg("a.d")
);
List<JPackage> out = root.getHierarchyPackages(packages);
List<JPackage> out = sources.getHierarchyPackages(packages);
assertEquals(out.size(), 1);
JPackage jpkg = out.get(0);
......@@ -64,7 +68,7 @@ public class JRootTest {
newPkg("a.b.p2"),
newPkg("a.b.p3")
);
List<JPackage> out = root.getHierarchyPackages(packages);
List<JPackage> out = sources.getHierarchyPackages(packages);
assertEquals(out.size(), 1);
JPackage jpkg = out.get(0);
......@@ -82,7 +86,7 @@ public class JRootTest {
newPkg("d.e"),
newPkg("d.f.a")
);
List<JPackage> out = root.getHierarchyPackages(packages);
List<JPackage> out = sources.getHierarchyPackages(packages);
assertEquals(out.size(), 2);
assertEquals(out.get(0).getName(), "a");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment