package gui;
import com.googlecode.dex2jar.reader.DexFileReader;
import parser.apk.APK;
import parser.dex.DexClass;
import utils.ComparatorClass;
import utils.FileDrop;
import utils.UtilLocal;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
// TODO 考虑增加 field 节点, method 节点, Fix native 方面没有解析的情况.
// MARK 发现如果是阅读代码工具的话,可能需要针对性,去写一个,而不是这么简陋的一个东西
public class CodeView extends JPanel {
final JTextArea jTextArea;
JTree treePkg;
public CodeView() {
setLayout(new BorderLayout(0, 0));
// 添加分割面板
final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setDividerLocation(250);
add(splitPane, BorderLayout.CENTER);
treePkg = new JTree();
treePkg.setRootVisible(false);
treePkg.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
JScrollPane jScrollPaneLeft = new JScrollPane(treePkg);
splitPane.setLeftComponent(jScrollPaneLeft);
jTextArea = new JTextArea();
jTextArea.setText("功能:提取类的字符串。\n" +
"\n" +
"使用方法:\n" +
"将 APK 文件拉入左边的框即可。\n" +
"点击相应的类,则可以显示该类中出现的字符串。");
JScrollPane jScrollPaneRight = new JScrollPane(jTextArea);
jScrollPaneRight.setViewportView(jTextArea);
splitPane.setRightComponent(jScrollPaneRight);
//监听拖动文件
new FileDrop(System.out, treePkg, /* dragBorder, */new FileDrop.Listener() {
@Override
public void filesDropped(File[] files) {
createNodes(files);
}
});
//左部被点击后动作
treePkg.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
JTree treeSource = (JTree) treeSelectionEvent.getSource();
TreePath[] treePaths = treeSource.getSelectionPaths();
if (treePaths == null) {
return;
}
jTextArea.setText("");
// get the current lead path
final TreePath treeSelectionPath = treeSelectionEvent.getNewLeadSelectionPath();
if (treeSelectionPath == null)
return;
final DefaultMutableTreeNode selectNode =
(DefaultMutableTreeNode) treeSelectionPath.getLastPathComponent();
if (selectNode.getUserObject() instanceof DexClass) {
DexClass dexClass = (DexClass) selectNode.getUserObject();
for (String method : dexClass.methodMap.keySet()) {
jTextArea.append("--->>> " + method + "\n");
jTextArea.append(dexClass.methodMap.get(method).replace("| |", "") + "\n");
jTextArea.append("\n");
}
}
}
});
}
/**
* @param parent 父节点
* @param packageName 查找或插入的节点名
* @return 查找或插入的节点
*/
public static DefaultMutableTreeNode findOrAddNode(TreePath parent, String packageName) {
final DefaultMutableTreeNode node = (DefaultMutableTreeNode) parent.getLastPathComponent();
for (final Enumeration e = node.children(); e.hasMoreElements(); ) {
final DefaultMutableTreeNode mutableTreeNode = (DefaultMutableTreeNode) e.nextElement();
if (packageName.equals(mutableTreeNode.toString()))
return mutableTreeNode;
}
final DefaultMutableTreeNode nodeCls = new DefaultMutableTreeNode(packageName);
node.add(nodeCls);
return nodeCls;
}
/**
* 获取类节点
*
* @param selectNode 类节点
* @return 类节点包含的节点列表
*/
ArrayList<ClassNode> getClassNodes(DefaultMutableTreeNode selectNode) {
ArrayList<ClassNode> classNodes = new ArrayList<>();
for (final Enumeration ee = selectNode.children(); ee.hasMoreElements(); ) {
final DefaultMutableTreeNode n = (DefaultMutableTreeNode) ee.nextElement();
if (n.isLeaf()) {
classNodes.add((ClassNode) n);
} else {
ArrayList<ClassNode> subClassNodes = getClassNodes(n);
classNodes.addAll(subClassNodes);
}
}
return classNodes;
}
/**
* 添加包和类节点(可以添加多个文件)
*
* @param files drop files...
*/
private void createNodes(File[] files) {
final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Files");
APK apk;
// 1.parse files
for (final File file : files) {
try {
apk = new APK(file);
} catch (IOException e) {
e.printStackTrace();
continue;
}
if (apk.getDexFileReader() == null)
continue;
// 创建根节点.
DefaultMutableTreeNode fileNode = new FileNode(apk.getDexFileReader(), file.getAbsolutePath());
rootNode.add(fileNode);
List<DexClass> classList = apk.getDexClasses();
Collections.sort(classList, new ComparatorClass());
for (final DexClass dexClass : classList) {
// 完整类名(com.pkg1.pkg2.cls;)
String className = dexClass.className;
// 获得节点字符串数组(com pkg1 pkg2 cls)
final String[] strings = className.substring(1, className.length() - 1).split("/");
if (UtilLocal.DEBUG) {
System.out.print(className + " ");
for (String name : strings) {
System.out.print(name + " ");
}
System.out.println();
}
int len = strings.length;
DefaultMutableTreeNode pkgNode = fileNode;
for (int i = 0; i < len - 1; i++) {
pkgNode = findOrAddNode(new TreePath(pkgNode), strings[i]);
}
ClassNode classNode = new ClassNode(dexClass);
pkgNode.add(classNode);
}
treePkg.setModel(new DefaultTreeModel(rootNode));
}
}
private class FileNode extends DefaultMutableTreeNode {
String fileName;
public FileNode(DexFileReader dexFileReader, String filePath) {
super(dexFileReader, true);
fileName = new File(filePath).getName();
}
@Override
public String toString() {
return fileName;
}
}
class ClassNode extends DefaultMutableTreeNode {
String className;
DexClass dexClass;
ClassNode(DexClass dexClass) {
super(dexClass, true);
final String[] strings = dexClass.className.substring
(1, dexClass.className.length() - 1).split("/");
className = strings[strings.length - 1];
this.dexClass = dexClass;
}
@Override
public String toString() {
return className;
}
}
}