/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package analyser.gui;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import analyser.logic.RefContext;
import analyser.logic.Reference;
import andreflect.ApkClassContext;
public class ClassTree extends JTree
{
private static final long serialVersionUID = -4687832080372737695L;
protected Map<Class<?>, JPopupMenu> m_popups = new HashMap<Class<?>, JPopupMenu>();
List<DefaultMutableTreeNode> markedNodes = new ArrayList<DefaultMutableTreeNode>();
final static Color COLOR_MARK = new Color(164, 232, 255);
Map<Class<?>, JPopupMenu> m_apk_popups = new HashMap<Class<?>, JPopupMenu>();
public void registerApkPopup(Class<?> nodeClass, JPopupMenu popup)
{
m_apk_popups.put(nodeClass, popup);
}
public void registerPopup(Class<?> nodeClass, JPopupMenu popup)
{
m_popups.put(nodeClass, popup);
}
public void markPaths(TreePath[] paths, boolean resetPreviousMarkings) {
if (resetPreviousMarkings) {
markedNodes.clear();
}
for (int i = 0; i < paths.length; i++) {
Object[] path = paths[i].getPath();
for (int j = 0; j < path.length; j++) {
markedNodes.add(((DefaultMutableTreeNode) path[j]));
}
}
repaint();
invalidate();
}
public boolean isMarked(DefaultMutableTreeNode o) {
return markedNodes.contains(o);
}
public void setSelected(boolean s)
{
if (s)
{
((ClassTreeRenderer) getCellRenderer()).setBackgroundSelectionColor(Color.LIGHT_GRAY);
}
else
{
((ClassTreeRenderer) getCellRenderer()).setBackgroundSelectionColor(COLOR_MARK);
}
}
public void refreshSelectedNode()
{
TreePath[] selPaths = getSelectionPaths();
if (selPaths != null && selPaths.length > 0)
{
expandPath(selPaths[0]);
collapse((DefaultMutableTreeNode) selPaths[0].getLastPathComponent());
}
}
public JPopupMenu getPopup(DefaultMutableTreeNode node)
{
Object userObj = null;
if (node != null) {
userObj = node.getUserObject();
}
if (userObj != null)
{
Reference ref = (Reference) userObj;
while (!(ref instanceof RefContext)) {
ref = (ref.getParent());
}
if (ref instanceof RefContext
&& ((RefContext) ref).getContext() instanceof ApkClassContext) {
return m_apk_popups.get(userObj.getClass());
}
return m_popups.get(userObj.getClass());
}
else
{
return m_popups.get(void.class);
}
}
public void collapseWholeTree()
{
Enumeration<TreePath> expands =
getExpandedDescendants(new TreePath(getModel().getRoot()));
while (expands != null && expands.hasMoreElements())
{
TreePath path = expands.nextElement();
int depth = path.getPathCount();
for (int i = 0; i < depth - 1; i++)
{
synchronized (getTreeLock()) {
collapsePath(path);
}
path = path.getParentPath();
}
}
}
public void collapse(DefaultMutableTreeNode node)
{
Enumeration<TreePath> expands =
getExpandedDescendants(new TreePath(node.getPath()));
while (expands != null && expands.hasMoreElements())
{
TreePath path = expands.nextElement();
synchronized (getTreeLock()) {
collapsePath(path);
}
}
}
/**
* Levels 0 - 4 where 0 == resource, 1 == package, 2 == class, 3 == method, 4 == invocation
* @param refInv
* @param level
* @return
*/
public TreePath getPath(Reference ref, Class<? extends Reference>... levels) {
//ref = null if tree is not the target
if (ref == null) {
return null;
}
LinkedList<Reference> refPath = new LinkedList<Reference>();
Reference parentRef = ref;
while (parentRef != null) {
boolean isInstance = false;
for (Class<? extends Reference> level : levels) {
if (level.isInstance(parentRef)) {
isInstance = true;
break;
}
}
if (isInstance) {
break;
}
parentRef = parentRef.getParent();
}
while (parentRef != null) {
refPath.add(parentRef);
parentRef = parentRef.getParent();
}
Collections.reverse(refPath);
List<TreeNode> path = new ArrayList<TreeNode>();
DefaultTreeModel model = (DefaultTreeModel) getModel();
TreeNode node = (TreeNode) model.getRoot();
Iterator<Reference> it = refPath.iterator();
path.add(node);
while (it.hasNext()) {
Reference refx = it.next();
node = findNode(node, refx);
path.add(node);
}
TreePath treePath = new TreePath(path.toArray());
return treePath;
}
public void findAndMarkNode(Object ref, int flags)
{
DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
findAndMarkNode(root, ref, flags);
}
protected boolean findAndMarkNode(DefaultMutableTreeNode parent, Object ref, int flags)
{
Object uo = parent.getUserObject();
if (uo == null) {
return false;
}
if (uo instanceof Reference && (ref.equals(((Reference) uo).getReferred()) ||
ref.equals(uo))) {
Reference tRef = (Reference) uo;
tRef.setFlags(tRef.getFlags() | flags);
return true;
}
if (ref.equals(uo)) {
return true;
}
int size = parent.getChildCount();
for (int i = 0; i < size; i++)
{
DefaultMutableTreeNode child =
(DefaultMutableTreeNode) parent.getChildAt(i);
if (findAndMarkNode(child, ref, flags))
{
if (uo instanceof Reference) {
Reference tRef = (Reference) uo;
tRef.setFlags(tRef.getFlags() | flags);
}
return true;
}
}
return false;
}
protected TreeNode findNode(TreeNode parent, Reference ref)
{
if (parent == null) {
return null;
}
TreeNode res = null;
int size = parent.getChildCount();
for (int i = 0; i < size; i++)
{
DefaultMutableTreeNode child =
(DefaultMutableTreeNode) parent.getChildAt(i);
if (child.getUserObject().equals(ref))
{
res = child;
break;
}
}
return res;
}
public ClassTree()
{
super();
}
/**
* @param value
*/
public ClassTree(Object[] value)
{
super(value);
}
/**
* @param newModel
*/
public ClassTree(TreeModel newModel)
{
super(newModel);
}
/**
* @param root
*/
public ClassTree(TreeNode root)
{
super(root);
}
/**
* @param root
* @param asksAllowsChildren
*/
public ClassTree(TreeNode root, boolean asksAllowsChildren)
{
super(root, asksAllowsChildren);
}
}