001: package tijmp.ui;
002:
003: import java.awt.Component;
004: import java.awt.Cursor;
005: import java.awt.GridBagConstraints;
006: import java.awt.GridBagLayout;
007: import java.awt.Insets;
008: import java.lang.reflect.Field;
009: import java.util.ArrayList;
010: import java.util.Collections;
011: import java.util.List;
012: import java.util.Map;
013: import javax.swing.JPanel;
014: import javax.swing.JScrollPane;
015: import javax.swing.JTree;
016: import javax.swing.event.TreeExpansionEvent;
017: import javax.swing.event.TreeWillExpandListener;
018: import javax.swing.tree.DefaultMutableTreeNode;
019: import javax.swing.tree.DefaultTreeCellRenderer;
020: import javax.swing.tree.DefaultTreeModel;
021: import javax.swing.tree.ExpandVetoException;
022: import javax.swing.tree.MutableTreeNode;
023: import javax.swing.tree.TreeNode;
024: import tijmp.OwnerInfo;
025: import tijmp.OwnerInfoHeader;
026: import tijmp.ProfilerHandler;
027:
028: /** A panel that shows object owner information
029: */
030: public class OwnerInfoTree extends JPanel {
031: public OwnerInfoTree(ProfilerHandler ph,
032: Map<Long, OwnerInfoHeader> owners, long[] startObjects) {
033: JTree tree = new JTree();
034: DefaultMutableTreeNode root = new DefaultMutableTreeNode();
035: Object[] os = ph.getObjectsForTags(startObjects);
036: for (int i = 0; i < startObjects.length; i++)
037: if (os[i] != null)
038: root.add(new OwnerTreeNode(ph, tree, owners,
039: startObjects[i], os[i], null));
040: tree.setModel(new DefaultTreeModel(root));
041: tree.addTreeWillExpandListener(new TreeExpander());
042:
043: JScrollPane sp = new JScrollPane(tree);
044: tree.setRootVisible(false);
045: tree.setCellRenderer(new OIRenderer());
046:
047: GridBagLayout gb = new GridBagLayout();
048: GridBagConstraints c = new GridBagConstraints();
049: setLayout(gb);
050: c.insets = new Insets(2, 2, 2, 2);
051:
052: c.gridx = 0;
053: c.gridy = 0;
054: c.weightx = 1;
055: c.weighty = 1;
056: c.fill = GridBagConstraints.BOTH;
057: add(sp, c);
058: }
059: }
060:
061: class TreeExpander implements TreeWillExpandListener {
062: public void treeWillExpand(TreeExpansionEvent e)
063: throws ExpandVetoException {
064: TreeNode tn = (TreeNode) e.getPath().getLastPathComponent();
065: if (tn instanceof OwnerTreeNode)
066: ((OwnerTreeNode) tn).addSubNodes();
067: }
068:
069: public void treeWillCollapse(TreeExpansionEvent e) {
070: TreeNode tn = (TreeNode) e.getPath().getLastPathComponent();
071: if (tn instanceof OwnerTreeNode)
072: ((OwnerTreeNode) tn).removeSubNodes();
073: }
074: }
075:
076: class OIRenderer extends DefaultTreeCellRenderer {
077: private static final int JVMTI_HEAP_REFERENCE_FIELD = 2;
078: private static final int JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3;
079: private static final int JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8;
080:
081: public Component getTreeCellRendererComponent(JTree tree,
082: Object value, boolean sel, boolean expanded, boolean leaf,
083: int row, boolean hasFocus) {
084: super .getTreeCellRendererComponent(tree, "", sel, expanded,
085: leaf, row, hasFocus);
086: if (value instanceof OwnerTreeNode) {
087: OwnerTreeNode otn = (OwnerTreeNode) value;
088: String cn = getClassText(otn);
089: String prefix = "";
090: OwnerInfo oi = otn.getOwnerInfo();
091: if (oi != null) {
092: Field f;
093: String fname;
094: switch (oi.getReferenceType()) {
095: case JVMTI_HEAP_REFERENCE_FIELD:
096: f = otn.getField(false);
097: fname = f == null ? "<null>?" : f.getName();
098: prefix = "field " + fname + "(" + oi.getIndex()
099: + ") in";
100: break;
101: case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT:
102: prefix = "position " + oi.getIndex() + " in";
103: break;
104: case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
105: f = otn.getField(true);
106: fname = f == null ? "<null>?" : f.getName();
107: prefix = "static field " + fname + "("
108: + oi.getIndex() + ") in";
109: break;
110: }
111: }
112: setText(prefix + " " + cn);
113: } else if (value instanceof DefaultMutableTreeNode) {
114: DefaultMutableTreeNode dmt = (DefaultMutableTreeNode) value;
115: setText(getClassText(dmt));
116: } else {
117: setText(value.toString());
118: }
119: return this ;
120: }
121:
122: private String getClassText(DefaultMutableTreeNode dmt) {
123: Object obj = dmt.getUserObject();
124: if (obj == null)
125: return "<null>";
126: Class<?> c = obj.getClass();
127: return Translator.translate(c);
128: }
129: }
130:
131: class OwnerTreeNode extends DefaultMutableTreeNode {
132: private ProfilerHandler ph;
133: private Map<Long, OwnerInfoHeader> owners;
134: private long tag;
135: private JTree tree;
136: private OwnerInfo oi;
137: private Field field;
138:
139: public OwnerTreeNode(ProfilerHandler ph, JTree tree,
140: Map<Long, OwnerInfoHeader> owners, long tag, Object node,
141: OwnerInfo oi) {
142: super (node, true);
143: this .ph = ph;
144: this .tree = tree;
145: this .tag = tag;
146: this .owners = owners;
147: this .oi = oi;
148: }
149:
150: public long getTag() {
151: return tag;
152: }
153:
154: public OwnerInfo getOwnerInfo() {
155: return oi;
156: }
157:
158: public Field getField(boolean objectIsClass) {
159: if (field != null)
160: return field;
161: if (oi == null)
162: return null;
163: Object o = getUserObject();
164: if (o == null)
165: return null;
166: List<Class<?>> ls = new ArrayList<Class<?>>();
167: List<Class<?>> is = new ArrayList<Class<?>>();
168: Class<?> c = o.getClass();
169: if (objectIsClass)
170: c = (Class<?>) o;
171: while (c != null) {
172: ls.add(c);
173: for (Class<?> i : c.getInterfaces()) {
174: if (!is.contains(i))
175: is.add(i);
176: }
177: c = c.getSuperclass();
178: }
179: ls.addAll(is);
180: Collections.reverse(ls);
181:
182: int fieldIndex = oi.getIndex();
183: for (Class<?> cs : ls) {
184: Field[] fs = cs.getDeclaredFields();
185: if (fs.length > fieldIndex)
186: return fs[fieldIndex];
187: fieldIndex -= fs.length;
188: }
189: return null;
190: }
191:
192: public int getChildCount() {
193: if (owners.containsKey(tag))
194: return Math.max(1, super .getChildCount());
195: return super .getChildCount();
196: }
197:
198: private boolean alreadyShownInParents(Object o) {
199: DefaultMutableTreeNode parent = this ;
200: while (parent != null) {
201: if (parent.getUserObject() == o)
202: return true;
203: parent = (DefaultMutableTreeNode) parent.getParent();
204: }
205: return false;
206: }
207:
208: public void addSubNodes() {
209: try {
210: tree.setCursor(new Cursor(Cursor.WAIT_CURSOR));
211: OwnerInfoHeader h = owners.get(tag);
212: long[] tags = h.getOwnerTags();
213: Object[] os = ph.getObjectsForTags(tags);
214: DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
215: for (int i = 0; i < tags.length; i++) {
216: if (os[i] != null) {
217: DefaultMutableTreeNode node = null;
218: if (alreadyShownInParents(os[i])) {
219: node = new DefaultMutableTreeNode(os[i], false);
220: } else {
221: OwnerInfo oi = h.getOwnerInfo(i);
222: node = new OwnerTreeNode(ph, tree, owners,
223: tags[i], os[i], oi);
224: }
225: model.insertNodeInto(node, this , super
226: .getChildCount());
227: }
228: }
229: } finally {
230: tree.setCursor(null); // inherit
231: }
232: }
233:
234: public void removeSubNodes() {
235: DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
236: while (super .getChildCount() > 0)
237: // use super since we lie on purpose :-)
238: model.removeNodeFromParent((MutableTreeNode) getChildAt(0));
239: }
240: }
|