gui: add definitions search window
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
package jadx.cli;
|
||||
|
||||
import jadx.api.Decompiler;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
@@ -26,7 +26,7 @@ public class JadxCLI {
|
||||
|
||||
static void processAndSave(JadxCLIArgs jadxArgs) throws JadxException {
|
||||
try {
|
||||
Decompiler jadx = new Decompiler(jadxArgs);
|
||||
JadxDecompiler jadx = new JadxDecompiler(jadxArgs);
|
||||
jadx.loadFiles(jadxArgs.getInput());
|
||||
jadx.setOutputDir(jadxArgs.getOutDir());
|
||||
jadx.save();
|
||||
|
||||
+6
-6
@@ -44,8 +44,8 @@ import org.slf4j.LoggerFactory;
|
||||
* }
|
||||
* </code></pre>
|
||||
*/
|
||||
public final class Decompiler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Decompiler.class);
|
||||
public final class JadxDecompiler {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxDecompiler.class);
|
||||
|
||||
private final IJadxArgs args;
|
||||
private final List<InputFile> inputFiles = new ArrayList<InputFile>();
|
||||
@@ -56,12 +56,12 @@ public final class Decompiler {
|
||||
private List<IDexTreeVisitor> passes;
|
||||
private List<JavaClass> classes;
|
||||
|
||||
public Decompiler() {
|
||||
public JadxDecompiler() {
|
||||
this.args = new DefaultJadxArgs();
|
||||
init();
|
||||
}
|
||||
|
||||
public Decompiler(IJadxArgs jadxArgs) {
|
||||
public JadxDecompiler(IJadxArgs jadxArgs) {
|
||||
this.args = jadxArgs;
|
||||
init();
|
||||
}
|
||||
@@ -143,7 +143,7 @@ public final class Decompiler {
|
||||
List<ClassNode> classNodeList = root.getClasses(false);
|
||||
List<JavaClass> clsList = new ArrayList<JavaClass>(classNodeList.size());
|
||||
for (ClassNode classNode : classNodeList) {
|
||||
clsList.add(new JavaClass(this, classNode));
|
||||
clsList.add(new JavaClass(classNode, this));
|
||||
}
|
||||
classes = Collections.unmodifiableList(clsList);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public final class Decompiler {
|
||||
Collections.sort(pkg.getClasses(), new Comparator<JavaClass>() {
|
||||
@Override
|
||||
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;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -15,18 +14,29 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
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 JavaClass parent;
|
||||
|
||||
private List<JavaClass> innerClasses = Collections.emptyList();
|
||||
private List<JavaField> fields = Collections.emptyList();
|
||||
private List<JavaMethod> methods = Collections.emptyList();
|
||||
|
||||
JavaClass(Decompiler decompiler, ClassNode classNode) {
|
||||
JavaClass(ClassNode classNode, JadxDecompiler decompiler) {
|
||||
this.decompiler = decompiler;
|
||||
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() {
|
||||
@@ -43,7 +53,7 @@ public final class JavaClass {
|
||||
|
||||
public void decompile() {
|
||||
if (decompiler == null) {
|
||||
throw new JadxRuntimeException("Can't decompile inner class");
|
||||
return;
|
||||
}
|
||||
if (cls.getCode() == null) {
|
||||
decompiler.processClass(cls);
|
||||
@@ -61,7 +71,7 @@ public final class JavaClass {
|
||||
List<JavaClass> list = new ArrayList<JavaClass>(inClsCount);
|
||||
for (ClassNode inner : cls.getInnerClasses()) {
|
||||
if (!inner.contains(AFlag.DONT_GENERATE)) {
|
||||
JavaClass javaClass = new JavaClass(null, inner);
|
||||
JavaClass javaClass = new JavaClass(inner, this);
|
||||
javaClass.load();
|
||||
list.add(javaClass);
|
||||
}
|
||||
@@ -74,7 +84,7 @@ public final class JavaClass {
|
||||
List<JavaField> flds = new ArrayList<JavaField>(fieldsCount);
|
||||
for (FieldNode f : cls.getFields()) {
|
||||
if (!f.contains(AFlag.DONT_GENERATE)) {
|
||||
flds.add(new JavaField(f));
|
||||
flds.add(new JavaField(f, this));
|
||||
}
|
||||
}
|
||||
this.fields = Collections.unmodifiableList(flds);
|
||||
@@ -135,31 +145,41 @@ public final class JavaClass {
|
||||
return cls.getCode().getLineMapping().get(decompiledLine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return cls.getShortName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return cls.getFullName();
|
||||
}
|
||||
|
||||
public String getShortName() {
|
||||
return cls.getShortName();
|
||||
}
|
||||
|
||||
public String getPackage() {
|
||||
return cls.getPackage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public AccessInfo getAccessInfo() {
|
||||
return cls.getAccessFlags();
|
||||
}
|
||||
|
||||
public List<JavaClass> getInnerClasses() {
|
||||
decompile();
|
||||
return innerClasses;
|
||||
}
|
||||
|
||||
public List<JavaField> getFields() {
|
||||
decompile();
|
||||
return fields;
|
||||
}
|
||||
|
||||
public List<JavaMethod> getMethods() {
|
||||
decompile();
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,18 +4,31 @@ import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
|
||||
public final class JavaField {
|
||||
public final class JavaField implements JavaNode {
|
||||
|
||||
private final FieldNode field;
|
||||
private final JavaClass parent;
|
||||
|
||||
public JavaField(FieldNode f) {
|
||||
JavaField(FieldNode f, JavaClass cls) {
|
||||
this.field = f;
|
||||
this.parent = cls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return field.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return parent.getFullName() + "." + field.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public AccessInfo getAccessFlags() {
|
||||
return field.getAccessFlags();
|
||||
}
|
||||
|
||||
@@ -6,19 +6,26 @@ import jadx.core.dex.nodes.MethodNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class JavaMethod {
|
||||
public final class JavaMethod implements JavaNode {
|
||||
private final MethodNode mth;
|
||||
private final JavaClass parent;
|
||||
|
||||
public JavaMethod(JavaClass cls, MethodNode m) {
|
||||
JavaMethod(JavaClass cls, MethodNode m) {
|
||||
this.parent = cls;
|
||||
this.mth = m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return mth.getMethodInfo().getName();
|
||||
return mth.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
return mth.getMethodInfo().getFullName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package jadx.api;
|
||||
|
||||
public interface JavaNode {
|
||||
|
||||
String getName();
|
||||
|
||||
String getFullName();
|
||||
|
||||
JavaClass getDeclaringClass();
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package jadx.api;
|
||||
|
||||
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 List<JavaClass> classes;
|
||||
|
||||
@@ -11,14 +11,26 @@ public final class JavaPackage implements Comparable<JavaPackage> {
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullName() {
|
||||
// TODO: store full package name
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<JavaClass> getClasses() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaClass getDeclaringClass() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(JavaPackage o) {
|
||||
return name.compareTo(o.name);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.tests
|
||||
|
||||
import jadx.api.Decompiler
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.api.IJadxArgs
|
||||
import jadx.core.dex.nodes.MethodNode
|
||||
import jadx.core.utils.ErrorsCounter
|
||||
@@ -12,7 +12,7 @@ class TestAPI extends Specification {
|
||||
|
||||
def "no loaded files"() {
|
||||
setup:
|
||||
def d = new Decompiler()
|
||||
def d = new JadxDecompiler()
|
||||
when:
|
||||
def classes = d.getClasses()
|
||||
def packages = d.getPackages()
|
||||
@@ -24,7 +24,7 @@ class TestAPI extends Specification {
|
||||
|
||||
def "save with no loaded files"() {
|
||||
when:
|
||||
new Decompiler().save()
|
||||
new JadxDecompiler().save()
|
||||
then:
|
||||
def e = thrown(JadxRuntimeException)
|
||||
e.message == "No loaded files"
|
||||
@@ -32,7 +32,7 @@ class TestAPI extends Specification {
|
||||
|
||||
def "load empty files list"() {
|
||||
when:
|
||||
new Decompiler().loadFiles(Collections.emptyList())
|
||||
new JadxDecompiler().loadFiles(Collections.emptyList())
|
||||
then:
|
||||
def e = thrown(JadxException)
|
||||
e.message == "Empty file list"
|
||||
@@ -40,14 +40,14 @@ class TestAPI extends Specification {
|
||||
|
||||
def "load null"() {
|
||||
when:
|
||||
new Decompiler().loadFile(null)
|
||||
new JadxDecompiler().loadFile(null)
|
||||
then:
|
||||
thrown(NullPointerException)
|
||||
}
|
||||
|
||||
def "load missing file"() {
|
||||
when:
|
||||
new Decompiler().loadFile(new File("_.dex"))
|
||||
new JadxDecompiler().loadFile(new File("_.dex"))
|
||||
then:
|
||||
def e = thrown(JadxException)
|
||||
e.message == "Error load file: _.dex"
|
||||
@@ -58,29 +58,29 @@ class TestAPI extends Specification {
|
||||
setup:
|
||||
def args = Mock(IJadxArgs)
|
||||
when:
|
||||
new Decompiler(args)
|
||||
new JadxDecompiler(args)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
def "get errors count for new decompiler"() {
|
||||
expect:
|
||||
new Decompiler().getErrorsCount() == 0
|
||||
new JadxDecompiler().getErrorsCount() == 0
|
||||
}
|
||||
|
||||
def "get errors count after one more init"() {
|
||||
setup:
|
||||
new Decompiler()
|
||||
new JadxDecompiler()
|
||||
def mth = Mock(MethodNode)
|
||||
when:
|
||||
ErrorsCounter.methodError(mth, "")
|
||||
def d = new Decompiler()
|
||||
def d = new JadxDecompiler()
|
||||
then:
|
||||
d.getErrorsCount() == 0
|
||||
}
|
||||
|
||||
def "decompiler toString()"() {
|
||||
expect:
|
||||
new Decompiler().toString() == "jadx decompiler"
|
||||
new JadxDecompiler().toString() == "jadx decompiler"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public abstract class InternalJadxTest extends TestUtils {
|
||||
protected String outDir = "test-out-tmp";
|
||||
|
||||
public ClassNode getClassNode(Class<?> clazz) {
|
||||
Decompiler d = new Decompiler();
|
||||
JadxDecompiler d = new JadxDecompiler();
|
||||
try {
|
||||
d.loadFile(getJarForClass(clazz));
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package jadx.gui;
|
||||
|
||||
import jadx.api.Decompiler;
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.JavaPackage;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
@@ -18,11 +18,11 @@ import org.slf4j.LoggerFactory;
|
||||
public class JadxWrapper {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JadxWrapper.class);
|
||||
|
||||
private final Decompiler decompiler;
|
||||
private final JadxDecompiler decompiler;
|
||||
private File openFile;
|
||||
|
||||
public JadxWrapper(IJadxArgs jadxArgs) {
|
||||
this.decompiler = new Decompiler(jadxArgs);
|
||||
this.decompiler = new JadxDecompiler(jadxArgs);
|
||||
}
|
||||
|
||||
public void openFile(File file) {
|
||||
@@ -53,7 +53,7 @@ public class JadxWrapper {
|
||||
progressMonitor.close();
|
||||
LOG.info("done");
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
LOG.error("Save interrupted", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -75,22 +75,25 @@ public class JClass extends JNode {
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
AccessInfo accessInfo = cls.getAccessInfo();
|
||||
|
||||
if (accessInfo.isEnum()) {
|
||||
return ICON_ENUM;
|
||||
} else if (accessInfo.isAnnotation()) {
|
||||
return ICON_ANNOTATION;
|
||||
} else if (accessInfo.isInterface()) {
|
||||
return ICON_INTERFACE;
|
||||
} else if (accessInfo.isProtected()) {
|
||||
return ICON_CLASS_PROTECTED;
|
||||
} else if (accessInfo.isPrivate()) {
|
||||
return ICON_CLASS_PRIVATE;
|
||||
} else if (accessInfo.isPublic()) {
|
||||
return ICON_CLASS;
|
||||
} else {
|
||||
return ICON_CLASS_DEFAULT;
|
||||
}
|
||||
if (accessInfo.isAnnotation()) {
|
||||
return ICON_ANNOTATION;
|
||||
}
|
||||
if (accessInfo.isInterface()) {
|
||||
return ICON_INTERFACE;
|
||||
}
|
||||
if (accessInfo.isProtected()) {
|
||||
return ICON_CLASS_PROTECTED;
|
||||
}
|
||||
if (accessInfo.isPrivate()) {
|
||||
return ICON_CLASS_PRIVATE;
|
||||
}
|
||||
if (accessInfo.isPublic()) {
|
||||
return ICON_CLASS;
|
||||
}
|
||||
return ICON_CLASS_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,7 +129,12 @@ public class JClass extends JNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return cls.getShortName();
|
||||
public String makeString() {
|
||||
return cls.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeLongString() {
|
||||
return cls.getFullName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,12 @@ public class JField extends JNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public String makeString() {
|
||||
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 {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
private String makeBaseString() {
|
||||
if (mth.isClassInit()) {
|
||||
return "{...}";
|
||||
}
|
||||
StringBuilder base = new StringBuilder();
|
||||
if (mth.isConstructor()) {
|
||||
base.append(mth.getDeclaringClass().getShortName());
|
||||
base.append(mth.getDeclaringClass().getName());
|
||||
} else {
|
||||
base.append(mth.getName());
|
||||
}
|
||||
@@ -76,6 +75,17 @@ public class JMethod extends JNode {
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,35 @@
|
||||
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.tree.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();
|
||||
|
||||
/**
|
||||
@@ -17,4 +42,15 @@ public abstract class JNode extends DefaultMutableTreeNode {
|
||||
public abstract int getLine();
|
||||
|
||||
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> {
|
||||
this.classes = new ArrayList<JClass>(1);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
public final void update() {
|
||||
removeAllChildren();
|
||||
for (JPackage pkg : innerPackages) {
|
||||
pkg.update();
|
||||
@@ -98,7 +98,12 @@ public class JPackage extends JNode implements Comparable<JPackage> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public String makeString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeLongString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class JRoot extends JNode {
|
||||
update();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
public final void update() {
|
||||
removeAllChildren();
|
||||
if (flatPackages) {
|
||||
for (JavaPackage pkg : wrapper.getPackages()) {
|
||||
@@ -85,7 +85,6 @@ public class JRoot extends JNode {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// use identity set for collect inner packages
|
||||
Set<JPackage> innerPackages = Collections.newSetFromMap(new IdentityHashMap<JPackage, Boolean>());
|
||||
for (JPackage pkg : pkgMap.values()) {
|
||||
@@ -150,7 +149,7 @@ public class JRoot extends JNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public String makeString() {
|
||||
File file = wrapper.getOpenFile();
|
||||
return file != null ? file.getName() : "File not open";
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class TextNode extends JNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public String makeString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
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 {
|
||||
private static final long serialVersionUID = 6312736869579635796L;
|
||||
|
||||
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 JClass cls;
|
||||
@@ -104,11 +102,11 @@ class CodeArea extends RSyntaxTextArea {
|
||||
}
|
||||
|
||||
void scrollToLine(int line) {
|
||||
line--;
|
||||
if (line < 0) {
|
||||
line = 0;
|
||||
int lineNum = line - 1;
|
||||
if (lineNum < 0) {
|
||||
lineNum = 0;
|
||||
}
|
||||
setCaretAtLine(line);
|
||||
setCaretAtLine(lineNum);
|
||||
centerCurrentLine();
|
||||
forceCurrentLineHighlightRepaint();
|
||||
}
|
||||
@@ -154,29 +152,30 @@ class CodeArea extends RSyntaxTextArea {
|
||||
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offset) {
|
||||
try {
|
||||
Token token = textArea.modelToToken(offset);
|
||||
if (token != null) {
|
||||
offset = token.getOffset();
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
final Position defPos = getPosition(jCls, textArea, offset);
|
||||
if (defPos != null) {
|
||||
final int sourceOffset = offset;
|
||||
return new LinkGeneratorResult() {
|
||||
@Override
|
||||
public HyperlinkEvent execute() {
|
||||
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
|
||||
defPos.getCls().getFullName());
|
||||
}
|
||||
final int sourceOffset = token.getOffset();
|
||||
final Position defPos = getPosition(jCls, textArea, sourceOffset);
|
||||
if (defPos == null) {
|
||||
return null;
|
||||
}
|
||||
return new LinkGeneratorResult() {
|
||||
@Override
|
||||
public HyperlinkEvent execute() {
|
||||
return new HyperlinkEvent(defPos, HyperlinkEvent.EventType.ACTIVATED, null,
|
||||
defPos.getCls().getFullName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceOffset() {
|
||||
return sourceOffset;
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public int getSourceOffset() {
|
||||
return sourceOffset;
|
||||
}
|
||||
};
|
||||
} catch (Exception e) {
|
||||
LOG.error("isLinkAtOffset error", e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,22 +23,24 @@ import java.awt.Rectangle;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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 final static int HEIGHT = Integer.MAX_VALUE - 1000000;
|
||||
private static final Border OUTER = new MatteBorder(0, 0, 0, 1, Color.LIGHT_GRAY);
|
||||
|
||||
public static final Color FOREGROUND = Color.GRAY;
|
||||
public static final Color BACKGROUND = CodeArea.BACKGROUND;
|
||||
public static final Color CURRENT_LINE_FOREGROUND = new Color(227, 0, 0);
|
||||
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 CURRENT_LINE_FOREGROUND = new Color(227, 0, 0);
|
||||
|
||||
private CodeArea codeArea;
|
||||
private boolean useSourceLines = true;
|
||||
|
||||
private int lastDigits;
|
||||
private int lastLine;
|
||||
private HashMap<String, FontMetrics> fonts;
|
||||
private Map<String, FontMetrics> fonts;
|
||||
|
||||
public LineNumbers(CodeArea component) {
|
||||
this.codeArea = component;
|
||||
|
||||
@@ -5,10 +5,12 @@ import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JRoot;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.Position;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenu;
|
||||
@@ -21,6 +23,7 @@ import javax.swing.JToggleButton;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.ProgressMonitor;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.TreeExpansionEvent;
|
||||
import javax.swing.event.TreeWillExpandListener;
|
||||
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
@@ -28,6 +31,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.ExpandVetoException;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
import java.awt.BorderLayout;
|
||||
@@ -59,8 +63,10 @@ public class MainWindow extends JFrame {
|
||||
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_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_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_FORWARD = Utils.openIcon("icon_forward");
|
||||
|
||||
@@ -116,11 +122,14 @@ public class MainWindow extends JFrame {
|
||||
tree.expandRow(0);
|
||||
}
|
||||
|
||||
private void toggleFlattenPackage() {
|
||||
private void toggleFlattenPackage(JToggleButton btn, JCheckBoxMenuItem menuItem) {
|
||||
Object root = treeModel.getRoot();
|
||||
if (root instanceof JRoot) {
|
||||
JRoot treeRoot = (JRoot) root;
|
||||
treeRoot.setFlatPackages(!treeRoot.isFlatPackages());
|
||||
boolean flatPkg = !treeRoot.isFlatPackages();
|
||||
btn.setSelected(flatPkg);
|
||||
menuItem.setState(flatPkg);
|
||||
treeRoot.setFlatPackages(flatPkg);
|
||||
treeModel.reload();
|
||||
tree.expandRow(0);
|
||||
}
|
||||
@@ -132,12 +141,28 @@ public class MainWindow extends JFrame {
|
||||
JNode node = (JNode) obj;
|
||||
JClass cls = node.getRootClass();
|
||||
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();
|
||||
if (codePanel != null) {
|
||||
codePanel.getSearchBar().toggle();
|
||||
@@ -147,7 +172,7 @@ public class MainWindow extends JFrame {
|
||||
private void initMenuAndToolbar() {
|
||||
JMenuBar menuBar = new JMenuBar();
|
||||
|
||||
JMenu file = new JMenu("File");
|
||||
JMenu file = new JMenu(NLS.str("menu.file"));
|
||||
file.setMnemonic(KeyEvent.VK_F);
|
||||
|
||||
JMenuItem exit = new JMenuItem(NLS.str("file.exit"), ICON_CLOSE);
|
||||
@@ -176,7 +201,65 @@ public class MainWindow extends JFrame {
|
||||
file.addSeparator();
|
||||
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(view);
|
||||
menuBar.add(nav);
|
||||
menuBar.add(help);
|
||||
setJMenuBar(menuBar);
|
||||
|
||||
JToolBar toolbar = new JToolBar();
|
||||
@@ -199,27 +282,40 @@ public class MainWindow extends JFrame {
|
||||
|
||||
toolbar.addSeparator();
|
||||
|
||||
final JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG);
|
||||
flatPkgButton.addActionListener(new ActionListener() {
|
||||
final JButton syncButton = new JButton(ICON_SYNC);
|
||||
syncButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggleFlattenPackage();
|
||||
syncWithEditor();
|
||||
}
|
||||
});
|
||||
flatPkgButton.setToolTipText(NLS.str("tree.flatten"));
|
||||
syncButton.setToolTipText(NLS.str("menu.sync"));
|
||||
toolbar.add(syncButton);
|
||||
|
||||
final JToggleButton flatPkgButton = new JToggleButton(ICON_FLAT_PKG);
|
||||
ActionListener flatPkgAction = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggleFlattenPackage(flatPkgButton, flatPkgMenuItem);
|
||||
}
|
||||
};
|
||||
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(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
toggleSearch();
|
||||
}
|
||||
});
|
||||
searchButton.setToolTipText(NLS.str("search"));
|
||||
searchButton.addActionListener(searchAction);
|
||||
searchButton.setToolTipText(NLS.str("menu.search"));
|
||||
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();
|
||||
|
||||
final JButton backButton = new JButton(ICON_BACK);
|
||||
@@ -251,7 +347,7 @@ public class MainWindow extends JFrame {
|
||||
splitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
|
||||
mainPanel.add(splitPane);
|
||||
|
||||
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Please open file");
|
||||
DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode(NLS.str("msg.open_file"));
|
||||
treeModel = new DefaultTreeModel(treeRoot);
|
||||
tree = new JTree(treeModel);
|
||||
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
|
||||
@@ -13,16 +13,20 @@ import javax.swing.text.BadLocationException;
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rtextarea.SearchContext;
|
||||
import org.fife.ui.rtextarea.SearchEngine;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class SearchBar extends JToolBar {
|
||||
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_WARN = new Color(0xFFFDD9);
|
||||
private static final Color COLOR_BG_NORMAL = new Color(0xFFFFFF);
|
||||
@@ -47,15 +51,7 @@ class SearchBar extends JToolBar {
|
||||
add(findLabel);
|
||||
|
||||
searchField = new JTextField(30);
|
||||
searchField.addKeyListener(new KeyListener() {
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
}
|
||||
|
||||
searchField.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
switch (e.getKeyCode()) {
|
||||
@@ -152,7 +148,7 @@ class SearchBar extends JToolBar {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean forward = (direction >= 0);
|
||||
boolean forward = direction >= 0;
|
||||
boolean matchCase = matchCaseCB.isSelected();
|
||||
boolean regex = regexCB.isSelected();
|
||||
boolean wholeWord = wholeWordCB.isSelected();
|
||||
@@ -179,7 +175,7 @@ class SearchBar extends JToolBar {
|
||||
rTextArea.setCaretPosition(rTextArea.getLineStartOffset(lineNum));
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
LOG.error("Caret move error", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
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 {
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
void showCode(final JClass cls, final int line) {
|
||||
showCode(new Position(cls, line));
|
||||
}
|
||||
|
||||
void showCode(final Position pos) {
|
||||
final CodePanel codePanel = getCodePanel(pos.getCls());
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
|
||||
@@ -11,7 +11,10 @@ public class NLS {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
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 {
|
||||
}
|
||||
Position position = (Position) obj;
|
||||
return line == position.line && cls.equals(position.cls);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,9 @@ public class Utils {
|
||||
private static final ImageIcon ICON_ABSTRACT = Utils.openIcon("abstract_co");
|
||||
private static final ImageIcon ICON_NATIVE = Utils.openIcon("native_co");
|
||||
|
||||
private Utils() {
|
||||
}
|
||||
|
||||
public static ImageIcon openIcon(String name) {
|
||||
String iconPath = "/icons-16/" + name + ".png";
|
||||
URL resource = Utils.class.getResource(iconPath);
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
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.save=Save file
|
||||
file.save_all=Save all
|
||||
@@ -5,7 +15,6 @@ file.save_all_msg=Select directory for save decompiled sources
|
||||
file.select=Select
|
||||
file.exit=Exit
|
||||
|
||||
tree.flatten=Flatten packages
|
||||
tree.loading=Loading...
|
||||
|
||||
search=Search
|
||||
@@ -23,3 +32,14 @@ tabs.closeAll=Close All
|
||||
|
||||
nav.back=Back
|
||||
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
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 282 B |
Binary file not shown.
|
After Width: | Height: | Size: 570 B |
@@ -10,7 +10,7 @@ public class Factory {
|
||||
return new JavaPackage(name, classes);
|
||||
}
|
||||
|
||||
public static JavaClass newClass(Decompiler decompiler, ClassNode classNode) {
|
||||
return new JavaClass(decompiler, classNode);
|
||||
public static JavaClass newClass(JadxDecompiler decompiler, ClassNode classNode) {
|
||||
return new JavaClass(classNode, decompiler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package jadx.gui.treemodel;
|
||||
|
||||
import jadx.api.Decompiler;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.Factory;
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.api.JavaClass;
|
||||
@@ -20,12 +20,12 @@ import static org.mockito.Mockito.mock;
|
||||
public class JRootTest {
|
||||
|
||||
private JRoot root;
|
||||
private Decompiler decompiler;
|
||||
private JadxDecompiler decompiler;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
root = new JRoot(mock(JadxWrapper.class));
|
||||
decompiler = new Decompiler(mock(IJadxArgs.class));
|
||||
decompiler = new JadxDecompiler(mock(IJadxArgs.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user