/**
*
*/
package de.rub.syssec.saaf.gui.editor;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import de.rub.syssec.saaf.gui.MainWindow;
import de.rub.syssec.saaf.model.application.ClassInterface;
import de.rub.syssec.saaf.model.application.CodeLineInterface;
import de.rub.syssec.saaf.model.application.MethodInterface;
/**
* Provides a tree of the methods of a class.
*
* @author Tilman Bender <tilman.bender@rub.de>
*
*/
public class OutlineView extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = -1744983593887093007L;
class MethodNode {
private MethodInterface method;
private String methodName;
private CodeLineInterface cl;
public MethodNode(MethodInterface method, String name,
CodeLineInterface cl) {
this.method = method;
methodName = name;
this.cl = cl;
}
public int getLine() {
return cl.getLineNr();
}
public MethodInterface getMethod() {
return method;
}
// this is shown in the tree
public String toString() {
if (method == null)
return methodName;
return method.getName() + " (" + method.getParameterString() + ")"
+ method.getReturnValueString();
}
}
private final class InternalMouseAdapter extends MouseAdapter {
ActionListener menuListener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (event.getActionCommand().startsWith("graph ")) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) ((JTree) event
.getSource()).getSelectionPath()
.getLastPathComponent();
Object nodeInfo = node.getUserObject();
MethodNode node_object = (MethodNode) nodeInfo;
MethodViewer methodViewer = new MethodViewer(
node_object.getMethod());
MainWindow.getDesktopPane().add(methodViewer);
try {
methodViewer.setSelected(true);
} catch (java.beans.PropertyVetoException e) {
}
}
}
};
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger())
myPopupEvent(e);
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger())
myPopupEvent(e);
}
private void myPopupEvent(MouseEvent e) {
int x = e.getX();
int y = e.getY();
JTree tree = (JTree) e.getSource();
TreePath path = tree.getPathForLocation(x, y);
if (path == null)
return;
tree.setSelectionPath(path);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
.getLastPathComponent();
Object nodeInfo = node.getUserObject();
MethodNode node_object = (MethodNode) nodeInfo;
// smali = node_object.methodName;
MethodInterface m = node_object.getMethod();
JPopupMenu popup = new JPopupMenu();
JMenuItem item = new JMenuItem("graph " + m);
item.addActionListener(menuListener);
popup.add(item);
popup.show(tree, x, y);
}
}
private final class SelectionListener implements TreeSelectionListener {
private final EditorModel editor;
private SelectionListener(EditorModel editor2) {
this.editor = editor2;
}
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath()
.getLastPathComponent();
Object nodeInfo = node.getUserObject();
if (node.isLeaf()) {
MethodNode nodeObject;
try {
nodeObject = (MethodNode) nodeInfo;
} catch (ClassCastException cce) {
// No file in this node
// (empty directory)
return;
}
try {
editor.setCurrentLine(nodeObject.getLine());
} catch (Exception e1) {
// logger.warn(
// "Problem during tree construction",
// e1);
}
}
}
}
private class MethodNameCellRenderer extends DefaultTreeCellRenderer {
private static final long serialVersionUID = 462485888657862971L;
@Override
public Component getTreeCellRendererComponent(JTree arg0, Object arg1,
boolean selected, boolean expanded, boolean leaf, int arg5,
boolean arg6) {
Component c = super.getTreeCellRendererComponent(arg0, arg1,
selected, expanded, leaf, arg5, arg6);
if (leaf) {
// TODO
// obtain the class for the file
MethodNode m = (MethodNode) ((DefaultMutableTreeNode)arg1).getUserObject();
MethodInterface method = m.getMethod();
// check if its obfuscated
if (method != null && method.isObfuscated()) {
// set foreground to red if it is
c.setForeground(Color.RED);
} else {
// otherwise set foreground to black
c.setForeground(Color.BLACK);
}
}
return c;
}
}
JTree outline;
public OutlineView(final EditorModel model) {
super();
setBackground(Color.GREEN);
this.setLayout(new GridBagLayout());
this.outline = new JTree();
this.outline.setCellRenderer(new MethodNameCellRenderer());
ClassInterface smaliClass = model.getCurrentClass();
if(smaliClass!=null)
{
this.outline.setModel(new DefaultTreeModel(buildTree(smaliClass)));
}else{
this.setVisible(false);
}
this.outline.addMouseListener(new InternalMouseAdapter());
this.outline.addTreeSelectionListener(new SelectionListener(model));
JScrollPane pane = new JScrollPane(outline);
GridBagConstraints constraints = new GridBagConstraints();
constraints.anchor = GridBagConstraints.FIRST_LINE_START;
constraints.fill = GridBagConstraints.BOTH;
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1.0;
constraints.weighty = 1.0;
this.add(pane,constraints);
}
/**
* @param smaliClass
* @return
*/
private DefaultMutableTreeNode buildTree(ClassInterface smaliClass) {
// children
DefaultMutableTreeNode file = new DefaultMutableTreeNode(
new MethodNode(null, smaliClass.getClassName(), smaliClass
.getAllCodeLines().getFirst()));
for (MethodInterface m : smaliClass.getMethods()) {
file.add(new DefaultMutableTreeNode(new MethodNode(m, m.getName()
+ " (" + m.getParameterString() + ")"
+ m.getReturnValueString(), m.getCodeLines().getFirst())));
}
return file;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
// if we are looking at a new class the method tree must be updated
if ("currentClass".equals(evt.getPropertyName())) {
if (evt.getNewValue() != null) {
ClassInterface c = (ClassInterface) evt.getNewValue();
TreeNode t = this.buildTree(c);
this.outline.setModel(new DefaultTreeModel(t));
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setVisible(true);
getParent().repaint();
}
});
}else
{
//we are not editing a class but something else (manifest,resource etc.)
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
//hide the panel and repaint the parent.
setVisible(false);
getParent().repaint();
}
});
}
}
}
}