Commit b67cd50e authored by Skylot's avatar Skylot

gui: add definitions search window

parent d2acaa03
package jadx.cli; package jadx.cli;
import jadx.api.Decompiler; import jadx.api.JadxDecompiler;
import jadx.core.utils.ErrorsCounter; import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxException;
...@@ -26,7 +26,7 @@ public class JadxCLI { ...@@ -26,7 +26,7 @@ public class JadxCLI {
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException { static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
try { try {
Decompiler jadx = new Decompiler(jadxArgs); JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
jadx.loadFiles(jadxArgs.getInput()); jadx.loadFiles(jadxArgs.getInput());
jadx.setOutputDir(jadxArgs.getOutDir()); jadx.setOutputDir(jadxArgs.getOutDir());
jadx.save(); jadx.save();
......
...@@ -44,8 +44,8 @@ import org.slf4j.LoggerFactory; ...@@ -44,8 +44,8 @@ import org.slf4j.LoggerFactory;
* } * }
* </code></pre> * </code></pre>
*/ */
public final class Decompiler { public final class JadxDecompiler {
private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class); private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
private final IJadxArgs args; private final IJadxArgs args;
private final List<InputFile> inputFiles = new ArrayList<InputFile>(); private final List<InputFile> inputFiles = new ArrayList<InputFile>();
...@@ -56,12 +56,12 @@ public final class Decompiler { ...@@ -56,12 +56,12 @@ public final class Decompiler {
private List<IDexTreeVisitor> passes; private List<IDexTreeVisitor> passes;
private List<JavaClass> classes; private List<JavaClass> classes;
public Decompiler() { public JadxDecompiler() {
this.args = new DefaultJadxArgs(); this.args = new DefaultJadxArgs();
init(); init();
} }
public Decompiler(IJadxArgs jadxArgs) { public JadxDecompiler(IJadxArgs jadxArgs) {
this.args = jadxArgs; this.args = jadxArgs;
init(); init();
} }
...@@ -143,7 +143,7 @@ public final class Decompiler { ...@@ -143,7 +143,7 @@ public final class Decompiler {
List<ClassNode> classNodeList = root.getClasses(false); List<ClassNode> classNodeList = root.getClasses(false);
List<JavaClass> clsList = new ArrayList<JavaClass>(classNodeList.size()); List<JavaClass> clsList = new ArrayList<JavaClass>(classNodeList.size());
for (ClassNode classNode : classNodeList) { for (ClassNode classNode : classNodeList) {
clsList.add(new JavaClass(this, classNode)); clsList.add(new JavaClass(classNode, this));
} }
classes = Collections.unmodifiableList(clsList); classes = Collections.unmodifiableList(clsList);
} }
...@@ -174,7 +174,7 @@ public final class Decompiler { ...@@ -174,7 +174,7 @@ public final class Decompiler {
Collections.sort(pkg.getClasses(), new Comparator<JavaClass>() { Collections.sort(pkg.getClasses(), new Comparator<JavaClass>() {
@Override @Override
public int compare(JavaClass o1, JavaClass o2) { public int compare(JavaClass o1, JavaClass o2) {
return o1.getShortName().compareTo(o2.getShortName()); return o1.getName().compareTo(o2.getName());
} }
}); });
} }
......
...@@ -7,7 +7,6 @@ import jadx.core.dex.info.AccessInfo; ...@@ -7,7 +7,6 @@ import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
...@@ -15,18 +14,29 @@ import java.util.Comparator; ...@@ -15,18 +14,29 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public final class JavaClass { public final class JavaClass implements JavaNode {
private final Decompiler decompiler; private final JadxDecompiler decompiler;
private final ClassNode cls; private final ClassNode cls;
private final JavaClass parent;
private List<JavaClass> innerClasses = Collections.emptyList(); private List<JavaClass> innerClasses = Collections.emptyList();
private List<JavaField> fields = Collections.emptyList(); private List<JavaField> fields = Collections.emptyList();
private List<JavaMethod> methods = Collections.emptyList(); private List<JavaMethod> methods = Collections.emptyList();
JavaClass(Decompiler decompiler, ClassNode classNode) { JavaClass(ClassNode classNode, JadxDecompiler decompiler) {
this.decompiler = decompiler; this.decompiler = decompiler;
this.cls = classNode; this.cls = classNode;
this.parent = null;
}
/**
* Inner classes constructor
*/
JavaClass(ClassNode classNode, JavaClass parent) {
this.decompiler = null;
this.cls = classNode;
this.parent = parent;
} }
public String getCode() { public String getCode() {
...@@ -43,7 +53,7 @@ public final class JavaClass { ...@@ -43,7 +53,7 @@ public final class JavaClass {
public void decompile() { public void decompile() {
if (decompiler == null) { if (decompiler == null) {
throw new JadxRuntimeException("Can't decompile inner class"); return;
} }
if (cls.getCode() == null) { if (cls.getCode() == null) {
decompiler.processClass(cls); decompiler.processClass(cls);
...@@ -61,7 +71,7 @@ public final class JavaClass { ...@@ -61,7 +71,7 @@ public final class JavaClass {
List<JavaClass> list = new ArrayList<JavaClass>(inClsCount); List<JavaClass> list = new ArrayList<JavaClass>(inClsCount);
for (ClassNode inner : cls.getInnerClasses()) { for (ClassNode inner : cls.getInnerClasses()) {
if (!inner.contains(AFlag.DONT_GENERATE)) { if (!inner.contains(AFlag.DONT_GENERATE)) {
JavaClass javaClass = new JavaClass(null, inner); JavaClass javaClass = new JavaClass(inner, this);
javaClass.load(); javaClass.load();
list.add(javaClass); list.add(javaClass);
} }
...@@ -74,7 +84,7 @@ public final class JavaClass { ...@@ -74,7 +84,7 @@ public final class JavaClass {
List<JavaField> flds = new ArrayList<JavaField>(fieldsCount); List<JavaField> flds = new ArrayList<JavaField>(fieldsCount);
for (FieldNode f : cls.getFields()) { for (FieldNode f : cls.getFields()) {
if (!f.contains(AFlag.DONT_GENERATE)) { if (!f.contains(AFlag.DONT_GENERATE)) {
flds.add(new JavaField(f)); flds.add(new JavaField(f, this));
} }
} }
this.fields = Collections.unmodifiableList(flds); this.fields = Collections.unmodifiableList(flds);
...@@ -135,31 +145,41 @@ public final class JavaClass { ...@@ -135,31 +145,41 @@ public final class JavaClass {
return cls.getCode().getLineMapping().get(decompiledLine); return cls.getCode().getLineMapping().get(decompiledLine);
} }
public String getFullName() { @Override
return cls.getFullName(); public String getName() {
return cls.getShortName();
} }
public String getShortName() { @Override
return cls.getShortName(); public String getFullName() {
return cls.getFullName();
} }
public String getPackage() { public String getPackage() {
return cls.getPackage(); return cls.getPackage();
} }
@Override
public JavaClass getDeclaringClass() {
return parent;
}
public AccessInfo getAccessInfo() { public AccessInfo getAccessInfo() {
return cls.getAccessFlags(); return cls.getAccessFlags();
} }
public List<JavaClass> getInnerClasses() { public List<JavaClass> getInnerClasses() {
decompile();
return innerClasses; return innerClasses;
} }
public List<JavaField> getFields() { public List<JavaField> getFields() {
decompile();
return fields; return fields;
} }
public List<JavaMethod> getMethods() { public List<JavaMethod> getMethods() {
decompile();
return methods; return methods;
} }
......
...@@ -4,18 +4,31 @@ import jadx.core.dex.info.AccessInfo; ...@@ -4,18 +4,31 @@ import jadx.core.dex.info.AccessInfo;
import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.FieldNode;
public final class JavaField { public final class JavaField implements JavaNode {
private final FieldNode field; private final FieldNode field;
private final JavaClass parent;
public JavaField(FieldNode f) { JavaField(FieldNode f, JavaClass cls) {
this.field = f; this.field = f;
this.parent = cls;
} }
@Override
public String getName() { public String getName() {
return field.getName(); return field.getName();
} }
@Override
public String getFullName() {
return parent.getFullName() + "." + field.getName();
}
@Override
public JavaClass getDeclaringClass() {
return parent;
}
public AccessInfo getAccessFlags() { public AccessInfo getAccessFlags() {
return field.getAccessFlags(); return field.getAccessFlags();
} }
......
...@@ -6,19 +6,26 @@ import jadx.core.dex.nodes.MethodNode; ...@@ -6,19 +6,26 @@ import jadx.core.dex.nodes.MethodNode;
import java.util.List; import java.util.List;
public final class JavaMethod { public final class JavaMethod implements JavaNode {
private final MethodNode mth; private final MethodNode mth;
private final JavaClass parent; private final JavaClass parent;
public JavaMethod(JavaClass cls, MethodNode m) { JavaMethod(JavaClass cls, MethodNode m) {
this.parent = cls; this.parent = cls;
this.mth = m; this.mth = m;
} }
@Override
public String getName() { public String getName() {
return mth.getMethodInfo().getName(); return mth.getName();
} }
@Override
public String getFullName() {
return mth.getMethodInfo().getFullName();
}
@Override
public JavaClass getDeclaringClass() { public JavaClass getDeclaringClass() {
return parent; return parent;
} }
......
package jadx.api;
public interface JavaNode {
String getName();
String getFullName();
JavaClass getDeclaringClass();
}
...@@ -2,7 +2,7 @@ package jadx.api; ...@@ -2,7 +2,7 @@ package jadx.api;
import java.util.List; import java.util.List;
public final class JavaPackage implements Comparable<JavaPackage> { public final class JavaPackage implements JavaNode, Comparable<JavaPackage> {
private final String name; private final String name;
private final List<JavaClass> classes; private final List<JavaClass> classes;
...@@ -11,15 +11,27 @@ public final class JavaPackage implements Comparable<JavaPackage> { ...@@ -11,15 +11,27 @@ public final class JavaPackage implements Comparable<JavaPackage> {
this.classes = classes; this.classes = classes;
} }
@Override
public String getName() { public String getName() {
return name; return name;
} }
@Override
public String getFullName() {
// TODO: store full package name
return name;
}
public List<JavaClass> getClasses() { public List<JavaClass> getClasses() {
return classes; return classes;
} }
@Override @Override
public JavaClass getDeclaringClass() {
return null;
}
@Override
public int compareTo(JavaPackage o) { public int compareTo(JavaPackage o) {
return name.compareTo(o.name); return name.compareTo(o.name);
} }
......
package jadx.tests package jadx.tests
import jadx.api.Decompiler import jadx.api.JadxDecompiler
import jadx.api.IJadxArgs import jadx.api.IJadxArgs
import jadx.core.dex.nodes.MethodNode import jadx.core.dex.nodes.MethodNode
import jadx.core.utils.ErrorsCounter import jadx.core.utils.ErrorsCounter
...@@ -12,7 +12,7 @@ class TestAPI extends Specification { ...@@ -12,7 +12,7 @@ class TestAPI extends Specification {
def "no loaded files"() { def "no loaded files"() {
setup: setup:
def d = new Decompiler() def d = new JadxDecompiler()
when: when:
def classes = d.getClasses() def classes = d.getClasses()
def packages = d.getPackages() def packages = d.getPackages()
...@@ -24,7 +24,7 @@ class TestAPI extends Specification { ...@@ -24,7 +24,7 @@ class TestAPI extends Specification {
def "save with no loaded files"() { def "save with no loaded files"() {
when: when:
new Decompiler().save() new JadxDecompiler().save()
then: then:
def e = thrown(JadxRuntimeException) def e = thrown(JadxRuntimeException)
e.message == "No loaded files" e.message == "No loaded files"
...@@ -32,7 +32,7 @@ class TestAPI extends Specification { ...@@ -32,7 +32,7 @@ class TestAPI extends Specification {
def "load empty files list"() { def "load empty files list"() {
when: when:
new Decompiler().loadFiles(Collections.emptyList()) new JadxDecompiler().loadFiles(Collections.emptyList())
then: then:
def e = thrown(JadxException) def e = thrown(JadxException)
e.message == "Empty file list" e.message == "Empty file list"
...@@ -40,14 +40,14 @@ class TestAPI extends Specification { ...@@ -40,14 +40,14 @@ class TestAPI extends Specification {
def "load null"() { def "load null"() {
when: when:
new Decompiler().loadFile(null) new JadxDecompiler().loadFile(null)
then: then:
thrown(NullPointerException) thrown(NullPointerException)
} }
def "load missing file"() { def "load missing file"() {
when: when:
new Decompiler().loadFile(new File("_.dex")) new JadxDecompiler().loadFile(new File("_.dex"))
then: then:
def e = thrown(JadxException) def e = thrown(JadxException)
e.message == "Error load file: _.dex" e.message == "Error load file: _.dex"
...@@ -58,29 +58,29 @@ class TestAPI extends Specification { ...@@ -58,29 +58,29 @@ class TestAPI extends Specification {
setup: setup:
def args = Mock(IJadxArgs) def args = Mock(IJadxArgs)
when: when:
new Decompiler(args) new JadxDecompiler(args)
then: then:
noExceptionThrown() noExceptionThrown()
} }
def "get errors count for new decompiler"() { def "get errors count for new decompiler"() {
expect: expect:
new Decompiler().getErrorsCount() == 0 new JadxDecompiler().getErrorsCount() == 0
} }
def "get errors count after one more init"() { def "get errors count after one more init"() {
setup: setup:
new Decompiler() new JadxDecompiler()
def mth = Mock(MethodNode) def mth = Mock(MethodNode)
when: when:
ErrorsCounter.methodError(mth, "") ErrorsCounter.methodError(mth, "")
def d = new Decompiler() def d = new JadxDecompiler()
then: then:
d.getErrorsCount() == 0 d.getErrorsCount() == 0
} }
def "decompiler toString()"() { def "decompiler toString()"() {
expect: expect:
new Decompiler().toString() == "jadx decompiler" new JadxDecompiler().toString() == "jadx decompiler"
} }
} }
...@@ -36,7 +36,7 @@ public abstract class InternalJadxTest extends TestUtils { ...@@ -36,7 +36,7 @@ public abstract class InternalJadxTest extends TestUtils {
protected String outDir = "test-out-tmp"; protected String outDir = "test-out-tmp";
public ClassNode getClassNode(Class<?> clazz) { public ClassNode getClassNode(Class<?> clazz) {
Decompiler d = new Decompiler(); JadxDecompiler d = new JadxDecompiler();
try { try {
d.loadFile(getJarForClass(clazz)); d.loadFile(getJarForClass(clazz));
} catch (Exception e) { } catch (Exception e) {
......
package jadx.gui; package jadx.gui;
import jadx.api.Decompiler;
import jadx.api.IJadxArgs; import jadx.api.IJadxArgs;
import jadx.api.JadxDecompiler;
import jadx.api.JavaClass; import jadx.api.JavaClass;
import jadx.api.JavaPackage; import jadx.api.JavaPackage;
import jadx.core.utils.exceptions.DecodeException; import jadx.core.utils.exceptions.DecodeException;
...@@ -18,11 +18,11 @@ import org.slf4j.LoggerFactory; ...@@ -18,11 +18,11 @@ import org.slf4j.LoggerFactory;
public class JadxWrapper { public class JadxWrapper {
private static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class); private static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class);
private final Decompiler decompiler; private final JadxDecompiler decompiler;
private File openFile; private File openFile;
public JadxWrapper(IJadxArgs jadxArgs) { public JadxWrapper(IJadxArgs jadxArgs) {
this.decompiler = new Decompiler(jadxArgs); this.decompiler = new JadxDecompiler(jadxArgs);
} }
public void openFile(File file) { public void openFile(File file) {
...@@ -53,7 +53,7 @@ public class JadxWrapper { ...@@ -53,7 +53,7 @@ public class JadxWrapper {
progressMonitor.close(); progressMonitor.close();
LOG.info("done"); LOG.info("done");
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); LOG.error("Save interrupted", e);
} }
} }
}; };
......
...@@ -75,22 +75,25 @@ public class JClass extends JNode { ...@@ -75,22 +75,25 @@ public class JClass extends JNode {
@Override @Override
public Icon getIcon() { public Icon getIcon() {
AccessInfo accessInfo = cls.getAccessInfo(); AccessInfo accessInfo = cls.getAccessInfo();
if (accessInfo.isEnum()) { if (accessInfo.isEnum()) {
return ICON_ENUM; return ICON_ENUM;
} else if (accessInfo.isAnnotation()) { }
if (accessInfo.isAnnotation()) {
return ICON_ANNOTATION; return ICON_ANNOTATION;
} else if (accessInfo.isInterface()) { }
if (accessInfo.isInterface()) {
return ICON_INTERFACE; return ICON_INTERFACE;
} else if (accessInfo.isProtected()) { }
if (accessInfo.isProtected()) {
return ICON_CLASS_PROTECTED; return ICON_CLASS_PROTECTED;
} else if (accessInfo.isPrivate()) { }
if (accessInfo.isPrivate()) {
return ICON_CLASS_PRIVATE; return ICON_CLASS_PRIVATE;
} else if (accessInfo.isPublic()) { }
if (accessInfo.isPublic()) {
return ICON_CLASS; return ICON_CLASS;
} else {
return ICON_CLASS_DEFAULT;
} }
return ICON_CLASS_DEFAULT;
} }
@Override @Override
...@@ -126,7 +129,12 @@ public class JClass extends JNode { ...@@ -126,7 +129,12 @@ public class JClass extends JNode {
} }
@Override @Override
public String toString() { public String makeString() {
return cls.getShortName(); return cls.getName();
}
@Override
public String makeLongString() {
return cls.getFullName();
} }
} }
...@@ -56,7 +56,12 @@ public class JField extends JNode { ...@@ -56,7 +56,12 @@ public class JField extends JNode {
} }
@Override @Override
public String toString() { public String makeString() {
return Utils.typeFormat(field.getName(), field.getType()); return Utils.typeFormat(field.getName(), field.getType());
} }
@Override
public String makeLongString() {
return Utils.typeFormat(field.getFullName(), field.getType());
}
} }
...@@ -57,14 +57,13 @@ public class JMethod extends JNode { ...@@ -57,14 +57,13 @@ public class JMethod extends JNode {
return icon; return icon;
} }
@Override private String makeBaseString() {
public String toString() {
if (mth.isClassInit()) { if (mth.isClassInit()) {
return "{...}"; return "{...}";
} }
StringBuilder base = new StringBuilder(); StringBuilder base = new StringBuilder();
if (mth.isConstructor()) { if (mth.isConstructor()) {
base.append(mth.getDeclaringClass().getShortName()); base.append(mth.getDeclaringClass().getName());
} else { } else {
base.append(mth.getName()); base.append(mth.getName());
} }
...@@ -76,6 +75,17 @@ public class JMethod extends JNode { ...@@ -76,6 +75,17 @@ public class JMethod extends JNode {
} }
} }
base.append(')'); base.append(')');
return Utils.typeFormat(base.toString(), mth.getReturnType()); return base.toString();
}
@Override
public String makeString() {
return Utils.typeFormat(makeBaseString(), mth.getReturnType());
}
@Override
public String makeLongString() {
String name = mth.getDeclaringClass().getFullName() + "." + makeBaseString();
return Utils.typeFormat(name, mth.getReturnType());
} }
} }
package jadx.gui.treemodel; package jadx.gui.treemodel;
import jadx.api.JavaClass;
import jadx.api.JavaField;
import jadx.api.JavaMethod;
import jadx.api.JavaNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultMutableTreeNode;
public abstract class JNode extends DefaultMutableTreeNode { public abstract class JNode extends DefaultMutableTreeNode {
public static JNode makeFrom(JavaNode node) {
if (node instanceof JavaClass) {
JClass p = (JClass) makeFrom(node.getDeclaringClass());
return new JClass((JavaClass) node, p);
}
if (node instanceof JavaMethod) {
JavaMethod mth = (JavaMethod) node;
return new JMethod(mth, new JClass(mth.getDeclaringClass()));
}
if (node instanceof JavaField) {
JavaField fld = (JavaField) node;
return new JField(fld, new JClass(fld.getDeclaringClass()));
}
if (node == null) {
return null;
}
throw new JadxRuntimeException("Unknown type for JavaNode: " + node.getClass());
}
public abstract JClass getJParent(); public abstract JClass getJParent();
/** /**
...@@ -17,4 +42,15 @@ public abstract class JNode extends DefaultMutableTreeNode { ...@@ -17,4 +42,15 @@ public abstract class JNode extends DefaultMutableTreeNode {
public abstract int getLine(); public abstract int getLine();
public abstract Icon getIcon(); public abstract Icon getIcon();
public abstract String makeString();
public String makeLongString() {
return makeString();
}
@Override
public String toString() {
return makeString();
}
} }
...@@ -33,7 +33,7 @@ public class JPackage extends JNode implements Comparable<JPackage> { ...@@ -33,7 +33,7 @@ public class JPackage extends JNode implements Comparable<JPackage> {
this.classes = new ArrayList<JClass>(1); this.classes = new ArrayList<JClass>(1);
} }
public void update() { public final void update() {
removeAllChildren(); removeAllChildren();
for (JPackage pkg : innerPackages) { for (JPackage pkg : innerPackages) {
pkg.update(); pkg.update();
...@@ -98,7 +98,12 @@ public class JPackage extends JNode implements Comparable<JPackage> { ...@@ -98,7 +98,12 @@ public class JPackage extends JNode implements Comparable<JPackage> {
} }
@Override @Override
public String toString() { public String makeString() {
return name;
}
@Override
public String makeLongString() {
return name; return name;
} }
} }
...@@ -30,7 +30,7 @@ public class JRoot extends JNode { ...@@ -30,7 +30,7 @@ public class JRoot extends JNode {
update(); update();
} }
public void update() { public final void update() {
removeAllChildren(); removeAllChildren();
if (flatPackages) { if (flatPackages) {
for (JavaPackage pkg : wrapper.getPackages()) { for (JavaPackage pkg : wrapper.getPackages()) {
...@@ -85,7 +85,6 @@ public class JRoot extends JNode { ...@@ -85,7 +85,6 @@ public class JRoot extends JNode {
it.remove(); it.remove();
} }
} }
// use identity set for collect inner packages // use identity set for collect inner packages
Set<JPackage> innerPackages = Collections.newSetFromMap(new IdentityHashMap<JPackage, Boolean>()); Set<JPackage> innerPackages = Collections.newSetFromMap(new IdentityHashMap<JPackage, Boolean>());
for (JPackage pkg : pkgMap.values()) { for (JPackage pkg : pkgMap.values()) {
...@@ -150,7 +149,7 @@ public class JRoot extends JNode { ...@@ -150,7 +149,7 @@ public class JRoot extends JNode {
} }
@Override @Override
public String toString() { public String makeString() {
File file = wrapper.getOpenFile(); File file = wrapper.getOpenFile();
return file != null ? file.getName() : "File not open"; return file != null ? file.getName() : "File not open";
} }
......
...@@ -28,7 +28,7 @@ public class TextNode extends JNode { ...@@ -28,7 +28,7 @@ public class TextNode extends JNode {
} }
@Override @Override
public String toString() { public String makeString() {
return label; return label;
} }
} }
package jadx.gui.ui;
import jadx.core.Jadx;
import jadx.gui.utils.NLS;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class AboutDialog extends JDialog {
private static final long serialVersionUID = 5763493590584039096L;
public AboutDialog() {
initUI();
}
public final void initUI() {
Font font = new Font("Serif", Font.BOLD, 13);
JLabel name = new JLabel("JADX");
name.setFont(font);
name.setAlignmentX(0.5f);
JLabel desc = new JLabel("Dex to Java decompiler");
desc.setFont(font);
desc.setAlignmentX(0.5f);
JLabel version = new JLabel("version: " + Jadx.getVersion());
version.setFont(font);
version.setAlignmentX(0.5f);
JPanel textPane = new JPanel();
textPane.setLayout(new BoxLayout(textPane, BoxLayout.PAGE_AXIS));
textPane.add(Box.createRigidArea(new Dimension(0, 10)));
textPane.add(name);
textPane.add(Box.createRigidArea(new Dimension(0, 10)));
textPane.add(desc);
textPane.add(Box.createRigidArea(new Dimension(0, 10)));
textPane.add(version);
textPane.add(Box.createRigidArea(new Dimension(0, 20)));
JButton close = new JButton(NLS.str("tabs.close"));
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
dispose();
}
});
close.setAlignmentX(0.5f);
Container contentPane = getContentPane();
contentPane.add(textPane, BorderLayout.CENTER);
contentPane.add(close, BorderLayout.PAGE_END);
setModalityType(ModalityType.APPLICATION_MODAL);
setTitle("About JADX");
pack();
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLocationRelativeTo(null);
}
}
...@@ -31,8 +31,6 @@ class CodeArea extends RSyntaxTextArea { ...@@ -31,8 +31,6 @@ class CodeArea extends RSyntaxTextArea {
private static final long serialVersionUID = 6312736869579635796L; private static final long serialVersionUID = 6312736869579635796L;
public static final Color BACKGROUND = new Color(0xf7f7f7); public static final Color BACKGROUND = new Color(0xf7f7f7);
private static final Color JUMP_FOREGROUND = new Color(0x785523);
private static final Color JUMP_BACKGROUND = new Color(0xE6E6FF);
private final CodePanel codePanel; private final CodePanel codePanel;
private final JClass cls; private final JClass cls;
...@@ -104,11 +102,11 @@ class CodeArea extends RSyntaxTextArea { ...@@ -104,11 +102,11 @@ class CodeArea extends RSyntaxTextArea {
} }
void scrollToLine(int line) { void scrollToLine(int line) {
line--; int lineNum = line - 1;
if (line < 0) { if (lineNum < 0) {
line = 0; lineNum = 0;
} }
setCaretAtLine(line); setCaretAtLine(lineNum);
centerCurrentLine(); centerCurrentLine();
forceCurrentLineHighlightRepaint(); forceCurrentLineHighlightRepaint();
} }
...@@ -154,29 +152,30 @@ class CodeArea extends RSyntaxTextArea { ...@@ -154,29 +152,30 @@ class CodeArea extends RSyntaxTextArea {
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) { public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) {
try { try {
Token token = textArea.modelToToken(offset); Token token = textArea.modelToToken(offset);
if (token != null) { if (token == null) {
offset = token.getOffset(); return null;
} }
final Position defPos = getPosition(jCls, textArea, offset); final int sourceOffset = token.getOffset();
if (defPos != null) { final Position defPos = getPosition(jCls, textArea, sourceOffset);
final int sourceOffset = offset; if (defPos == null) {
return new LinkGeneratorResult() { return null;
@Override
public HyperlinkEvent execute() {
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
defPos.getCls().getFullName());
}
@Override
public int getSourceOffset() {
return sourceOffset;
}
};
} }
return new LinkGeneratorResult() {
@Override
public HyperlinkEvent execute() {
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
defPos.getCls().getFullName());
}
@Override
public int getSourceOffset() {
return sourceOffset;
}
};
} catch (Exception e) { } catch (Exception e) {
LOG.error("isLinkAtOffset error", e); LOG.error("isLinkAtOffset error", e);
return null;
} }
return null;
} }
@Override @Override
......
...@@ -23,22 +23,24 @@ import java.awt.Rectangle; ...@@ -23,22 +23,24 @@ import java.awt.Rectangle;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
public class LineNumbers extends JPanel implements CaretListener { public class LineNumbers extends JPanel implements CaretListener {
private static final long serialVersionUID = -4978268673635308190L;
private final static Border OUTER = new MatteBorder(0, 0, 0, 1, Color.LIGHT_GRAY); private static final Border OUTER = new MatteBorder(0, 0, 0, 1, Color.LIGHT_GRAY);
private final static int HEIGHT = Integer.MAX_VALUE - 1000000;
public static final Color FOREGROUND = Color.GRAY; private static final int HEIGHT = Integer.MAX_VALUE - 1000000;
public static final Color BACKGROUND = CodeArea.BACKGROUND; private static final Color FOREGROUND = Color.GRAY;
public static final Color CURRENT_LINE_FOREGROUND = new Color(227, 0, 0); private static final Color BACKGROUND = CodeArea.BACKGROUND;
private static final Color CURRENT_LINE_FOREGROUND = new Color(227, 0, 0);
private CodeArea codeArea; private CodeArea codeArea;
private boolean useSourceLines = true; private boolean useSourceLines = true;
private int lastDigits; private int lastDigits;
private int lastLine; private int lastLine;
private HashMap<String, FontMetrics> fonts; private Map<String, FontMetrics> fonts;
public LineNumbers(CodeArea component) { public LineNumbers(CodeArea component) {
this.codeArea = component; this.codeArea = component;
......
...@@ -5,10 +5,12 @@ import jadx.gui.treemodel.JClass; ...@@ -5,10 +5,12 @@ import jadx.gui.treemodel.JClass;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JRoot; import jadx.gui.treemodel.JRoot;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
import jadx.gui.utils.Utils; import jadx.gui.utils.Utils;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JMenu; import javax.swing.JMenu;
...@@ -21,6 +23,7 @@ import javax.swing.JToggleButton; ...@@ -21,6 +23,7 @@ import javax.swing.JToggleButton;
import javax.swing.JToolBar; import javax.swing.JToolBar;
import javax.swing.JTree; import javax.swing.JTree;
import javax.swing.ProgressMonitor; import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener; import javax.swing.event.TreeWillExpandListener;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
...@@ -28,6 +31,7 @@ import javax.swing.tree.DefaultMutableTreeNode; ...@@ -28,6 +31,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.ExpandVetoException; import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath; import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel; import javax.swing.tree.TreeSelectionModel;
import java.awt.BorderLayout; import java.awt.BorderLayout;
...@@ -59,8 +63,10 @@ public class MainWindow extends JFrame { ...@@ -59,8 +63,10 @@ public class MainWindow extends JFrame {
private static final ImageIcon ICON_OPEN = Utils.openIcon("folder"); private static final ImageIcon ICON_OPEN = Utils.openIcon("folder");
private static final ImageIcon ICON_SAVE_ALL = Utils.openIcon("disk_multiple"); private static final ImageIcon ICON_SAVE_ALL = Utils.openIcon("disk_multiple");
private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross"); private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross");
private static final ImageIcon ICON_SYNC = Utils.openIcon("sync");
private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj"); private static final ImageIcon ICON_FLAT_PKG = Utils.openIcon("empty_logical_package_obj");
private static final ImageIcon ICON_SEARCH = Utils.openIcon("magnifier"); private static final ImageIcon ICON_SEARCH = Utils.openIcon("wand");
private static final ImageIcon ICON_FIND = Utils.openIcon("magnifier");
private static final ImageIcon ICON_BACK = Utils.openIcon("icon_back"); private static final ImageIcon ICON_BACK = Utils.openIcon("icon_back");
private static final ImageIcon ICON_FORWARD = Utils.openIcon("icon_forward"); private static final ImageIcon ICON_FORWARD = Utils.openIcon("icon_forward");
...@@ -116,11 +122,14 @@ public class MainWindow extends JFrame { ...@@ -116,11 +122,14 @@ public class MainWindow extends JFrame {
tree.expandRow(0); tree.expandRow(0);
} }
private void toggleFlattenPackage() { private void toggleFlattenPackage(JToggleButton btn, JCheckBoxMenuItem menuItem) {
Object root = treeModel.getRoot(); Object root = treeModel.getRoot();
if (root instanceof JRoot) { if (root instanceof JRoot) {
JRoot treeRoot = (JRoot) root; JRoot treeRoot = (JRoot) root;
treeRoot.setFlatPackages(!treeRoot.isFlatPackages()); boolean flatPkg = !treeRoot.isFlatPackages();
btn.setSelected(flatPkg);
menuItem.setState(flatPkg);
treeRoot.setFlatPackages(flatPkg);
treeModel.reload(); treeModel.reload();
tree.expandRow(0); tree.expandRow(0);
} }
...@@ -132,12 +141,28 @@ public class MainWindow extends JFrame { ...@@ -132,12 +141,28 @@ public class MainWindow extends JFrame {
JNode node = (JNode) obj; JNode node = (JNode) obj;
JClass cls = node.getRootClass(); JClass cls = node.getRootClass();
if (cls != null) { if (cls != null) {
tabbedPane.showCode(cls, node.getLine()); tabbedPane.showCode(new Position(cls, node.getLine()));
} }
} }
} }
private void toggleSearch() { private void syncWithEditor() {
CodePanel selectedCodePanel = tabbedPane.getSelectedCodePanel();
if (selectedCodePanel == null) {
return;
}
JClass jCls = selectedCodePanel.getCls();
TreeNode[] pathNodes = treeModel.getPathToRoot(jCls);
if (pathNodes == null) {
return;
}
TreePath path = new TreePath(pathNodes);
tree.setSelectionPath(path);
tree.expandPath(path);
tree.makeVisible(path);
}
private void toggleFind() {
CodePanel codePanel = tabbedPane.getSelectedCodePanel(); CodePanel codePanel = tabbedPane.getSelectedCodePanel();
if (codePanel != null) { if (codePanel != null) {
codePanel.getSearchBar().toggle(); codePanel.getSearchBar().toggle();
...@@ -147,7 +172,7 @@ public class MainWindow extends JFrame { ...@@ -147,7 +172,7 @@ public class MainWindow extends JFrame {
private void initMenuAndToolbar() { private void initMenuAndToolbar() {
JMenuBar menuBar = new JMenuBar(); JMenuBar menuBar = new JMenuBar();
JMenu file = new JMenu("File"); JMenu file = new JMenu(NLS.str("menu.file"));
file.setMnemonic(KeyEvent.VK_F); file.setMnemonic(KeyEvent.VK_F);
JMenuItem exit = new JMenuItem(NLS.str("file.exit"), ICON_CLOSE); JMenuItem exit = new JMenuItem(NLS.str("file.exit"), ICON_CLOSE);
...@@ -176,7 +201,65 @@ public class MainWindow extends JFrame { ...@@ -176,7 +201,65 @@ public class MainWindow extends JFrame {
file.addSeparator(); file.addSeparator();
file.add(exit); file.add(exit);
JMenu view = new JMenu(NLS.str("menu.view"));
view.setMnemonic(KeyEvent.VK_V);
final JCheckBoxMenuItem flatPkgMenuItem = new JCheckBoxMenuItem(NLS.str("menu.flatten"), ICON_FLAT_PKG);
view.add(flatPkgMenuItem);
JMenuItem syncItem = new JMenuItem(NLS.str("menu.sync"), ICON_SYNC);
view.add(syncItem);
syncItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
syncWithEditor();
}
});
JMenu nav = new JMenu(NLS.str("menu.navigation"));
nav.setMnemonic(KeyEvent.VK_N);
JMenuItem search = new JMenuItem(NLS.str("menu.search"), ICON_SEARCH);
nav.add(search);
ActionListener searchAction = new ActionListener() {
public void actionPerformed(ActionEvent event) {
final SearchDialog dialog = new SearchDialog(MainWindow.this, tabbedPane, wrapper);
dialog.prepare();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
dialog.setVisible(true);
}
});
}
};
search.addActionListener(searchAction);
JMenuItem find = new JMenuItem(NLS.str("menu.find_in_file"), ICON_FIND);
nav.add(find);
ActionListener findAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
toggleFind();
}
};
find.addActionListener(findAction);
JMenu help = new JMenu(NLS.str("menu.help"));
help.setMnemonic(KeyEvent.VK_H);
JMenuItem about = new JMenuItem(NLS.str("menu.about"));
help.add(about);
about.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
AboutDialog ad = new AboutDialog();
ad.setVisible(true);
}
});
menuBar.add(file); menuBar.add(file);
menuBar.add(view);
menuBar.add(nav);
menuBar.add(help);
setJMenuBar(menuBar); setJMenuBar(menuBar);
JToolBar toolbar = new JToolBar(); JToolBar toolbar = new JToolBar();
...@@ -199,27 +282,40 @@ public class MainWindow extends JFrame { ...@@ -199,27 +282,40 @@ public class MainWindow extends JFrame {
toolbar.addSeparator(); toolbar.addSeparator();
final JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG); final JButton syncButton = new JButton(ICON_SYNC);
flatPkgButton.addActionListener(new ActionListener() { syncButton.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
toggleFlattenPackage(); syncWithEditor();
} }
}); });
flatPkgButton.setToolTipText(NLS.str("tree.flatten")); syncButton.setToolTipText(NLS.str("menu.sync"));
toolbar.add(flatPkgButton); toolbar.add(syncButton);
toolbar.addSeparator();
final JButton searchButton = new JButton(ICON_SEARCH); final JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG);
searchButton.addActionListener(new ActionListener() { ActionListener flatPkgAction = new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
toggleSearch(); toggleFlattenPackage(flatPkgButton, flatPkgMenuItem);
} }
}); };
searchButton.setToolTipText(NLS.str("search")); flatPkgButton.addActionListener(flatPkgAction);
flatPkgMenuItem.addActionListener(flatPkgAction);
flatPkgButton.setToolTipText(NLS.str("menu.flatten"));
toolbar.add(flatPkgButton);
toolbar.addSeparator();
final JButton searchButton = new JButton(ICON_SEARCH);
searchButton.addActionListener(searchAction);
searchButton.setToolTipText(NLS.str("menu.search"));
toolbar.add(searchButton); toolbar.add(searchButton);
final JButton findButton = new JButton(ICON_FIND);
findButton.addActionListener(findAction);
findButton.setToolTipText(NLS.str("menu.find_in_file"));
toolbar.add(findButton);
toolbar.addSeparator(); toolbar.addSeparator();
final JButton backButton = new JButton(ICON_BACK); final JButton backButton = new JButton(ICON_BACK);
...@@ -251,7 +347,7 @@ public class MainWindow extends JFrame { ...@@ -251,7 +347,7 @@ public class MainWindow extends JFrame {
splitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT); splitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
mainPanel.add(splitPane); mainPanel.add(splitPane);
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Please open file"); DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode(NLS.str("msg.open_file"));
treeModel = new DefaultTreeModel(treeRoot); treeModel = new DefaultTreeModel(treeRoot);
tree = new JTree(treeModel); tree = new JTree(treeModel);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
......
...@@ -13,16 +13,20 @@ import javax.swing.text.BadLocationException; ...@@ -13,16 +13,20 @@ import javax.swing.text.BadLocationException;
import java.awt.Color; import java.awt.Color;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.SearchContext; import org.fife.ui.rtextarea.SearchContext;
import org.fife.ui.rtextarea.SearchEngine; import org.fife.ui.rtextarea.SearchEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class SearchBar extends JToolBar { class SearchBar extends JToolBar {
private static final long serialVersionUID = 1836871286618633003L; private static final long serialVersionUID = 1836871286618633003L;
private static final Logger LOG = LoggerFactory.getLogger(SearchDialog.class);
private static final Color COLOR_BG_ERROR = new Color(0xFFDFDE); private static final Color COLOR_BG_ERROR = new Color(0xFFDFDE);
private static final Color COLOR_BG_WARN = new Color(0xFFFDD9); private static final Color COLOR_BG_WARN = new Color(0xFFFDD9);
private static final Color COLOR_BG_NORMAL = new Color(0xFFFFFF); private static final Color COLOR_BG_NORMAL = new Color(0xFFFFFF);
...@@ -47,15 +51,7 @@ class SearchBar extends JToolBar { ...@@ -47,15 +51,7 @@ class SearchBar extends JToolBar {
add(findLabel); add(findLabel);
searchField = new JTextField(30); searchField = new JTextField(30);
searchField.addKeyListener(new KeyListener() { searchField.addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override @Override
public void keyReleased(KeyEvent e) { public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) { switch (e.getKeyCode()) {
...@@ -152,7 +148,7 @@ class SearchBar extends JToolBar { ...@@ -152,7 +148,7 @@ class SearchBar extends JToolBar {
return; return;
} }
boolean forward = (direction >= 0); boolean forward = direction >= 0;
boolean matchCase = matchCaseCB.isSelected(); boolean matchCase = matchCaseCB.isSelected();
boolean regex = regexCB.isSelected(); boolean regex = regexCB.isSelected();
boolean wholeWord = wholeWordCB.isSelected(); boolean wholeWord = wholeWordCB.isSelected();
...@@ -179,7 +175,7 @@ class SearchBar extends JToolBar { ...@@ -179,7 +175,7 @@ class SearchBar extends JToolBar {
rTextArea.setCaretPosition(rTextArea.getLineStartOffset(lineNum)); rTextArea.setCaretPosition(rTextArea.getLineStartOffset(lineNum));
} }
} catch (BadLocationException e) { } catch (BadLocationException e) {
e.printStackTrace(); LOG.error("Caret move error", e);
} }
} }
......
package jadx.gui.ui;
import jadx.api.JavaClass;
import jadx.api.JavaField;
import jadx.api.JavaMethod;
import jadx.api.JavaNode;
import jadx.gui.JadxWrapper;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.NLS;
import jadx.gui.utils.NameIndex;
import jadx.gui.utils.Position;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
public class SearchDialog extends JDialog {
private static final long serialVersionUID = -5105405456969134105L;
private static final int MAX_RESULTS_COUNT = 100;
private static enum SearchOptions {
CLASS,
METHOD,
FIELD,
CODE
}
private static final Set<SearchOptions> OPTIONS =
EnumSet.of(SearchOptions.CLASS, SearchOptions.METHOD, SearchOptions.FIELD);
private final TabbedPane tabbedPane;
private final JadxWrapper wrapper;
private NameIndex<JavaNode> index;
private JTextField searchField;
private ResultsModel resultsModel;
private JList resultsList;
private JProgressBar busyBar;
public SearchDialog(Frame owner, TabbedPane tabbedPane, JadxWrapper wrapper) {
super(owner);
this.tabbedPane = tabbedPane;
this.wrapper = wrapper;
initUI();
}
public void prepare() {
LoadTask task = new LoadTask();
task.init();
task.execute();
}
private void loadData() {
index = new NameIndex<JavaNode>();
for (JavaClass cls : wrapper.getClasses()) {
indexClass(cls);
}
}
private synchronized void performSearch() {
String text = searchField.getText();
List<JavaNode> results;
if (text == null || text.isEmpty() || index == null) {
results = Collections.emptyList();
} else {
results = index.search(text);
}
resultsModel.setResults(results);
}
private void openSelectedItem() {
int selectedId = resultsList.getSelectedIndex();
if (selectedId == -1) {
return;
}
JNode node = (JNode) resultsModel.get(selectedId);
tabbedPane.showCode(new Position(node.getRootClass(), node.getLine()));
dispose();
}
private void indexClass(JavaClass cls) {
if (OPTIONS.contains(SearchOptions.CLASS)) {
index.add(cls.getFullName(), cls);
}
if (OPTIONS.contains(SearchOptions.METHOD)) {
for (JavaMethod mth : cls.getMethods()) {
index.add(mth.getFullName(), mth);
}
}
if (OPTIONS.contains(SearchOptions.FIELD)) {
for (JavaField fld : cls.getFields()) {
index.add(fld.getFullName(), fld);
}
}
if (OPTIONS.contains(SearchOptions.CODE)) {
String code = cls.getCode();
index.add(code, cls);
}
for (JavaClass innerCls : cls.getInnerClasses()) {
indexClass(innerCls);
}
}
private class LoadTask extends SwingWorker<Void, Void> {
public void init() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
busyBar.setVisible(true);
searchField.setEnabled(false);
resultsList.setEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
});
}
@Override
public Void doInBackground() {
loadData();
return null;
}
@Override
public void done() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setCursor(null);
searchField.setEnabled(true);
resultsList.setEnabled(true);
busyBar.setVisible(false);
}
});
}
}
private static class ResultsModel extends DefaultListModel {
private static final long serialVersionUID = -7821286846923903208L;
private void setResults(List<JavaNode> results) {
removeAllElements();
if (results.isEmpty()) {
return;
}
int count = Math.min(results.size(), MAX_RESULTS_COUNT);
for (int i = 0; i < count; i++) {
addElement(JNode.makeFrom(results.get(i)));
}
}
}
private static class ResultsCellRenderer implements ListCellRenderer {
private final Color selectedBackground;
private final Color selectedForeground;
ResultsCellRenderer() {
UIDefaults defaults = UIManager.getDefaults();
selectedBackground = defaults.getColor("List.selectionBackground");
selectedForeground = defaults.getColor("List.selectionForeground");
}
@Override
public Component getListCellRendererComponent(JList list,
Object obj, int index, boolean isSelected, boolean cellHasFocus) {
if (!(obj instanceof JNode)) {
return null;
}
JNode value = (JNode) obj;
JLabel label = new JLabel();
label.setOpaque(true);
label.setIcon(value.getIcon());
label.setText(value.makeLongString());
if (isSelected) {
label.setBackground(selectedBackground);
label.setForeground(selectedForeground);
}
return label;
}
}
private class SearchFieldListener implements DocumentListener {
public void changedUpdate(DocumentEvent e) {
performSearch();
}
public void removeUpdate(DocumentEvent e) {
performSearch();
}
public void insertUpdate(DocumentEvent e) {
performSearch();
}
}
private void initUI() {
JLabel findLabel = new JLabel(NLS.str("search_dialog.open_by_name"));
searchField = new JTextField();
searchField.setAlignmentX(LEFT_ALIGNMENT);
searchField.getDocument().addDocumentListener(new SearchFieldListener());
JCheckBox clsChBox = makeOptionsCheckBox(NLS.str("search_dialog.class"), SearchOptions.CLASS);
JCheckBox mthChBox = makeOptionsCheckBox(NLS.str("search_dialog.method"), SearchOptions.METHOD);
JCheckBox fldChBox = makeOptionsCheckBox(NLS.str("search_dialog.field"), SearchOptions.FIELD);
JCheckBox codeChBox = makeOptionsCheckBox(NLS.str("search_dialog.code"), SearchOptions.CODE);
codeChBox.setEnabled(false);
resultsModel = new ResultsModel();
resultsList = new JList(resultsModel);
resultsList.setCellRenderer(new ResultsCellRenderer());
resultsList.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 2) {
openSelectedItem();
}
}
});
JPanel searchOptions = new JPanel(new FlowLayout(FlowLayout.LEFT));
searchOptions.setBorder(BorderFactory.createTitledBorder(NLS.str("search_dialog.search_in")));
searchOptions.add(clsChBox);
searchOptions.add(mthChBox);
searchOptions.add(fldChBox);
searchOptions.add(codeChBox);
searchOptions.setAlignmentX(LEFT_ALIGNMENT);
JPanel searchPane = new JPanel();
searchPane.setLayout(new BoxLayout(searchPane, BoxLayout.PAGE_AXIS));
findLabel.setLabelFor(searchField);
searchPane.add(findLabel);
searchPane.add(Box.createRigidArea(new Dimension(0, 5)));
searchPane.add(searchField);
searchPane.add(Box.createRigidArea(new Dimension(0, 5)));
searchPane.add(searchOptions);
searchPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
listPane.add(new JScrollPane(resultsList));
listPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
busyBar = new JProgressBar();
busyBar.setIndeterminate(true);
busyBar.setVisible(false);
//Create and initialize the buttons.
JButton cancelButton = new JButton(NLS.str("search_dialog.cancel"));
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
dispose();
}
});
JButton openBtn = new JButton(NLS.str("search_dialog.open"));
openBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
openSelectedItem();
}
});
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
buttonPane.add(busyBar);
searchPane.add(Box.createRigidArea(new Dimension(5, 0)));
buttonPane.add(Box.createHorizontalGlue());
buttonPane.add(openBtn);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
buttonPane.add(cancelButton);
Container contentPane = getContentPane();
contentPane.add(searchPane, BorderLayout.PAGE_START);
contentPane.add(listPane, BorderLayout.CENTER);
contentPane.add(buttonPane, BorderLayout.PAGE_END);
getRootPane().setDefaultButton(openBtn);
setTitle(NLS.str("menu.search"));
pack();
setSize(700, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setModalityType(ModalityType.APPLICATION_MODAL);
}
private JCheckBox makeOptionsCheckBox(String name, final SearchOptions opt) {
JCheckBox chBox = new JCheckBox(name);
chBox.setAlignmentX(LEFT_ALIGNMENT);
chBox.setSelected(OPTIONS.contains(opt));
chBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
OPTIONS.add(opt);
} else {
OPTIONS.remove(opt);
}
loadData();
performSearch();
}
});
return chBox;
}
}
...@@ -65,10 +65,6 @@ class TabbedPane extends JTabbedPane { ...@@ -65,10 +65,6 @@ class TabbedPane extends JTabbedPane {
return mainWindow; return mainWindow;
} }
void showCode(final JClass cls, final int line) {
showCode(new Position(cls, line));
}
void showCode(final Position pos) { void showCode(final Position pos) {
final CodePanel codePanel = getCodePanel(pos.getCls()); final CodePanel codePanel = getCodePanel(pos.getCls());
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
......
...@@ -11,7 +11,10 @@ public class NLS { ...@@ -11,7 +11,10 @@ public class NLS {
load(new Locale("en", "US")); load(new Locale("en", "US"));
} }
public static void load(Locale locale) { private NLS() {
}
private static void load(Locale locale) {
messages = ResourceBundle.getBundle("i18n/Messages", locale); messages = ResourceBundle.getBundle("i18n/Messages", locale);
} }
......
package jadx.gui.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class NameIndex<T> {
private final List<String> strings = new ArrayList<String>();
private final List<T> objects = new ArrayList<T>();
public void add(String name, T obj) {
strings.add(name);
objects.add(obj);
}
public List<T> search(String text) {
List<T> results = new ArrayList<T>();
int count = strings.size();
for (int i = 0; i < count; i++) {
String name = strings.get(i);
if (name.contains(text)) {
results.add(objects.get(i));
}
}
return results.isEmpty() ? Collections.<T>emptyList() : results;
}
}
...@@ -35,7 +35,6 @@ public class Position { ...@@ -35,7 +35,6 @@ public class Position {
} }
Position position = (Position) obj; Position position = (Position) obj;
return line == position.line && cls.equals(position.cls); return line == position.line && cls.equals(position.cls);
} }
@Override @Override
......
...@@ -18,6 +18,9 @@ public class Utils { ...@@ -18,6 +18,9 @@ public class Utils {
private static final ImageIcon ICON_ABSTRACT = Utils.openIcon("abstract_co"); private static final ImageIcon ICON_ABSTRACT = Utils.openIcon("abstract_co");
private static final ImageIcon ICON_NATIVE = Utils.openIcon("native_co"); private static final ImageIcon ICON_NATIVE = Utils.openIcon("native_co");
private Utils() {
}
public static ImageIcon openIcon(String name) { public static ImageIcon openIcon(String name) {
String iconPath = "/icons-16/" + name + ".png"; String iconPath = "/icons-16/" + name + ".png";
URL resource = Utils.class.getResource(iconPath); URL resource = Utils.class.getResource(iconPath);
......
menu.file=File
menu.view=View
menu.sync=Sync with editor
menu.flatten=Show flatten packages
menu.navigation=Navigation
menu.search=Search ...
menu.find_in_file=Find in ...
menu.help=Help
menu.about=About
file.open=Open file file.open=Open file
file.save=Save file file.save=Save file
file.save_all=Save all file.save_all=Save all
...@@ -5,7 +15,6 @@ file.save_all_msg=Select directory for save decompiled sources ...@@ -5,7 +15,6 @@ file.save_all_msg=Select directory for save decompiled sources
file.select=Select file.select=Select
file.exit=Exit file.exit=Exit
tree.flatten=Flatten packages
tree.loading=Loading... tree.loading=Loading...
search=Search search=Search
...@@ -23,3 +32,14 @@ tabs.closeAll=Close All ...@@ -23,3 +32,14 @@ tabs.closeAll=Close All
nav.back=Back nav.back=Back
nav.forward=Forward nav.forward=Forward
search_dialog.open=Open
search_dialog.cancel=Cancel
search_dialog.open_by_name=Search for text\:
search_dialog.search_in=Search definitions of \:
search_dialog.class=Class
search_dialog.method=Method
search_dialog.field=Field
search_dialog.code=Code
msg.open_file=Please open file
...@@ -10,7 +10,7 @@ public class Factory { ...@@ -10,7 +10,7 @@ public class Factory {
return new JavaPackage(name, classes); return new JavaPackage(name, classes);
} }
public static JavaClass newClass(Decompiler decompiler, ClassNode classNode) { public static JavaClass newClass(JadxDecompiler decompiler, ClassNode classNode) {
return new JavaClass(decompiler, classNode); return new JavaClass(classNode, decompiler);
} }
} }
package jadx.gui.treemodel; package jadx.gui.treemodel;
import jadx.api.Decompiler; import jadx.api.JadxDecompiler;
import jadx.api.Factory; import jadx.api.Factory;
import jadx.api.IJadxArgs; import jadx.api.IJadxArgs;
import jadx.api.JavaClass; import jadx.api.JavaClass;
...@@ -20,12 +20,12 @@ import static org.mockito.Mockito.mock; ...@@ -20,12 +20,12 @@ import static org.mockito.Mockito.mock;
public class JRootTest { public class JRootTest {
private JRoot root; private JRoot root;
private Decompiler decompiler; private JadxDecompiler decompiler;
@Before @Before
public void init() { public void init() {
root = new JRoot(mock(JadxWrapper.class)); root = new JRoot(mock(JadxWrapper.class));
decompiler = new Decompiler(mock(IJadxArgs.class)); decompiler = new JadxDecompiler(mock(IJadxArgs.class));
} }
@Test @Test
......
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