001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.ui.memory;
042:
043: import org.netbeans.lib.profiler.global.CommonConstants;
044: import org.netbeans.lib.profiler.results.memory.*;
045: import org.netbeans.lib.profiler.ui.UIConstants;
046: import org.netbeans.lib.profiler.ui.UIUtils;
047: import org.netbeans.lib.profiler.ui.components.*;
048: import org.netbeans.lib.profiler.ui.components.JTreeTable;
049: import org.netbeans.lib.profiler.ui.components.table.CustomBarCellRenderer;
050: import org.netbeans.lib.profiler.ui.components.table.SortableTableModel;
051: import org.netbeans.lib.profiler.ui.components.treetable.AbstractTreeTableModel;
052: import org.netbeans.lib.profiler.ui.components.treetable.ExtendedTreeTableModel;
053: import org.netbeans.lib.profiler.ui.components.treetable.JTreeTablePanel;
054: import org.netbeans.lib.profiler.ui.components.treetable.TreeTableModel;
055: import org.netbeans.lib.profiler.utils.StringUtils;
056: import java.awt.*;
057: import java.awt.event.ActionEvent;
058: import java.awt.event.InputEvent;
059: import java.awt.event.KeyAdapter;
060: import java.awt.event.KeyEvent;
061: import java.awt.event.MouseAdapter;
062: import java.awt.event.MouseEvent;
063: import java.awt.image.BufferedImage;
064: import java.util.HashSet;
065: import java.util.ResourceBundle;
066: import java.util.Set;
067: import javax.swing.*;
068: import javax.swing.event.ListSelectionEvent;
069: import javax.swing.event.ListSelectionListener;
070: import javax.swing.table.TableColumnModel;
071:
072: /**
073: * A panel containing a reverse call graph for all allocations of instances of a given class
074: *
075: * @author Misha Dmitriev
076: * @author Jiri Sedlacek
077: */
078: public class SnapshotReverseMemCallGraphPanel extends
079: ReverseMemCallGraphPanel {
080: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
081:
082: // -----
083: // I18N String constants
084: private static final ResourceBundle messages = ResourceBundle
085: .getBundle("org.netbeans.lib.profiler.ui.memory.Bundle"); // NOI18N
086: private static final String NO_STACK_TRACES_MSG = messages
087: .getString("SnapshotReverseMemCallGraphPanel_NoStackTracesMsg"); // NOI18N
088: private static final String TREETABLE_ACCESS_NAME = messages
089: .getString("SnapshotReverseMemCallGraphPanel_TreeTableAccessName"); // NOI18N
090: // -----
091:
092: //~ Instance fields ----------------------------------------------------------------------------------------------------------
093:
094: protected int classId;
095: private AbstractTreeTableModel abstractTreeTableModel;
096: private JPanel noContentPanel;
097: private MemoryCCTManager callGraphManager;
098: private MemoryResultsSnapshot snapshot;
099: private boolean initialSortingOrder;
100: private int initialSortingColumn;
101:
102: //~ Constructors -------------------------------------------------------------------------------------------------------------
103:
104: public SnapshotReverseMemCallGraphPanel(
105: MemoryResultsSnapshot snapshot,
106: MemoryResUserActionsHandler actionsHandler) {
107: super (actionsHandler,
108: snapshot instanceof LivenessMemoryResultsSnapshot);
109: this .snapshot = snapshot;
110:
111: noContentPanel = new JPanel();
112: noContentPanel.setLayout(new BorderLayout());
113: noContentPanel.setBorder(BorderFactory.createEmptyBorder(12,
114: 12, 12, 12));
115:
116: JLabel noContentIcon = new JLabel(
117: new javax.swing.ImageIcon(
118: getClass()
119: .getResource(
120: "/org/netbeans/lib/profiler/ui/resources/takeSnapshotMem.png"))); // NOI18N
121: noContentIcon.setBorder(BorderFactory.createEmptyBorder(10, 10,
122: 0, 5));
123: noContentIcon.setVerticalAlignment(SwingConstants.TOP);
124: noContentIcon.setEnabled(false);
125:
126: JTextArea noContentText = new JTextArea(NO_STACK_TRACES_MSG);
127: noContentText.setBorder(BorderFactory.createEmptyBorder(10, 0,
128: 0, 0));
129: noContentText.setFont(noContentText.getFont().deriveFont(14));
130: noContentText.setEditable(false);
131: noContentText.setEnabled(false);
132: noContentText.setWrapStyleWord(true);
133: noContentText.setLineWrap(true);
134: noContentText.setBackground(noContentPanel.getBackground());
135:
136: JPanel containerPanel = new JPanel(new BorderLayout());
137: containerPanel.add(noContentIcon, BorderLayout.WEST);
138: containerPanel.add(noContentText, BorderLayout.CENTER);
139: noContentPanel.add(containerPanel, BorderLayout.NORTH);
140:
141: setDefaultSorting();
142: }
143:
144: //~ Methods ------------------------------------------------------------------------------------------------------------------
145:
146: public void setClassId(int classId) {
147: this .classId = classId;
148: callGraphManager = new MemoryCCTManager(snapshot, classId, true);
149:
150: if (!callGraphManager.isEmpty()) {
151: customBarCellRenderer = new CustomBarCellRenderer(
152: 0,
153: ((MemoryCCTManager) callGraphManager).getRootNode().totalObjSize);
154: columnRenderers[1] = customBarCellRenderer;
155: }
156: }
157:
158: public BufferedImage getCurrentViewScreenshot(
159: boolean onlyVisibleArea) {
160: if ((treeTablePanel == null) || (treeTable == null)) {
161: return null;
162: }
163:
164: if (onlyVisibleArea) {
165: return UIUtils.createScreenshot(treeTablePanel
166: .getScrollPane());
167: } else {
168: return UIUtils.createScreenshot(treeTable);
169: }
170: }
171:
172: // NOTE: this method only sets initialSortingColumn and initialSortingOrder, it doesn't refresh UI!
173: public void setDefaultSorting() {
174: setSorting(1, SortableTableModel.SORT_ORDER_DESC);
175: }
176:
177: public boolean isEmpty() {
178: return (callGraphManager == null) || callGraphManager.isEmpty();
179: }
180:
181: // NOTE: this method only sets initialSortingColumn and initialSortingOrder, it doesn't refresh UI!
182: public void setSorting(int sColumn, boolean sOrder) {
183: if (sColumn == CommonConstants.SORTING_COLUMN_DEFAULT) {
184: setDefaultSorting();
185: } else {
186: initialSortingColumn = sColumn;
187: initialSortingOrder = sOrder;
188: }
189: }
190:
191: public boolean fitsVisibleArea() {
192: return !treeTablePanel.getScrollPane().getVerticalScrollBar()
193: .isEnabled();
194: }
195:
196: public boolean hasView() {
197: return treeTable != null;
198: }
199:
200: public void prepareResults() {
201: if ((callGraphManager == null) || callGraphManager.isEmpty()) {
202: removeAll();
203: add(noContentPanel, BorderLayout.CENTER);
204: } else {
205: abstractTreeTableModel = new AbstractTreeTableModel(
206: callGraphManager.getRootNode(),
207: initialSortingColumn, initialSortingOrder) {
208: public int getColumnCount() {
209: return columnNames.length;
210: }
211:
212: public String getColumnName(int column) {
213: return columnNames[column];
214: }
215:
216: public Class getColumnClass(int column) {
217: if (column == 0) {
218: return TreeTableModel.class;
219: } else {
220: return Object.class;
221: }
222: }
223:
224: public Object getValueAt(Object node, int column) {
225: long value;
226:
227: if (extendedResults) {
228: PresoObjLivenessCCTNode pNode = (PresoObjLivenessCCTNode) node;
229:
230: switch (column) {
231: case 0:
232: return pNode.getNodeName();
233: case 1:
234: return new Long(pNode.totalObjSize);
235: case 2:
236: value = ((PresoObjLivenessCCTNode) root).totalObjSize;
237:
238: return intFormat.format(pNode.totalObjSize)
239: + " B ("
240: + ((value == 0) ? "-%"
241: : percentFormat
242: .format((float) pNode.totalObjSize
243: / (float) value))
244: + ")"; // NOI18N
245: case 3:
246: value = ((PresoObjLivenessCCTNode) root).nLiveObjects;
247:
248: return intFormat.format(pNode.nLiveObjects)
249: + " ("
250: + ((value == 0) ? "-%"
251: : percentFormat
252: .format((float) pNode.nLiveObjects
253: / (float) value))
254: + ")"; // NOI18N
255: case 4:
256: return intFormat.format(pNode.nCalls);
257: case 5:
258: return StringUtils
259: .floatPerCentToString(pNode.avgObjectAge);
260: case 6:
261: return intFormat.format(pNode.survGen);
262: }
263: } else {
264: PresoObjAllocCCTNode pNode = (PresoObjAllocCCTNode) node;
265:
266: switch (column) {
267: case 0:
268: return pNode.getNodeName();
269: case 1:
270: return new Long(pNode.totalObjSize);
271: case 2:
272: value = ((PresoObjAllocCCTNode) root).totalObjSize;
273:
274: return intFormat.format(pNode.totalObjSize)
275: + " B ("
276: + ((value == 0) ? "-%"
277: : percentFormat
278: .format((float) pNode.totalObjSize
279: / (float) value))
280: + ")"; // NOI18N
281: case 3:
282: value = ((PresoObjAllocCCTNode) root).nCalls;
283:
284: return intFormat.format(pNode.nCalls)
285: + " ("
286: + ((value == 0) ? "-%"
287: : percentFormat
288: .format((float) pNode.nCalls
289: / (float) value))
290: + ")"; // NOI18N
291: }
292: }
293:
294: return null;
295: }
296:
297: public String getColumnToolTipText(int col) {
298: return columnToolTips[col];
299: }
300:
301: public void sortByColumn(int column, boolean order) {
302: if (extendedResults) {
303: PresoObjLivenessCCTNode pRoot = (PresoObjLivenessCCTNode) root;
304:
305: switch (column) {
306: case 0:
307: pRoot
308: .sortChildren(
309: PresoObjLivenessCCTNode.SORT_BY_NAME,
310: order);
311:
312: break;
313: case 1:
314: case 2:
315: pRoot
316: .sortChildren(
317: PresoObjLivenessCCTNode.SORT_BY_LIVE_OBJ_SIZE,
318: order);
319:
320: break;
321: case 3:
322: pRoot
323: .sortChildren(
324: PresoObjLivenessCCTNode.SORT_BY_LIVE_OBJ_NUMBER,
325: order);
326:
327: break;
328: case 4:
329: pRoot
330: .sortChildren(
331: PresoObjLivenessCCTNode.SORT_BY_ALLOC_OBJ,
332: order);
333:
334: break;
335: case 5:
336: pRoot
337: .sortChildren(
338: PresoObjLivenessCCTNode.SORT_BY_AVG_AGE,
339: order);
340:
341: break;
342: case 6:
343: pRoot
344: .sortChildren(
345: PresoObjLivenessCCTNode.SORT_BY_SURV_GEN,
346: order);
347:
348: break;
349: }
350: } else {
351: PresoObjAllocCCTNode pRoot = (PresoObjAllocCCTNode) root;
352:
353: switch (column) {
354: case 0:
355: pRoot.sortChildren(
356: PresoObjAllocCCTNode.SORT_BY_NAME,
357: order);
358:
359: break;
360: case 1:
361: case 2:
362: pRoot
363: .sortChildren(
364: PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_SIZE,
365: order);
366:
367: break;
368: case 3:
369: pRoot
370: .sortChildren(
371: PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_NUMBER,
372: order);
373:
374: break;
375: }
376: }
377: };
378:
379: public boolean getInitialSorting(int column) {
380: switch (column) {
381: case 0:
382: return true;
383: default:
384: return false;
385: }
386: }
387: };
388: treeTableModel = new ExtendedTreeTableModel(
389: abstractTreeTableModel);
390:
391: treeTable = new JTreeTable(treeTableModel) {
392: public void doLayout() {
393: int columnsWidthsSum = 0;
394: int realFirstColumn = -1;
395:
396: int index;
397: TableColumnModel colModel = getColumnModel();
398:
399: for (int i = 0; i < treeTableModel.getColumnCount(); i++) {
400: index = treeTableModel.getRealColumn(i);
401:
402: if (index == 0) {
403: realFirstColumn = i;
404: } else {
405: columnsWidthsSum += colModel.getColumn(i)
406: .getPreferredWidth();
407: }
408: }
409:
410: if (realFirstColumn != -1) {
411: colModel.getColumn(realFirstColumn)
412: .setPreferredWidth(
413: Math.max(getWidth()
414: - columnsWidthsSum,
415: minNamesColumnWidth));
416: }
417:
418: super .doLayout();
419: };
420: };
421: treeTable.getAccessibleContext().setAccessibleName(
422: TREETABLE_ACCESS_NAME);
423:
424: treeTable.setRowSelectionAllowed(true);
425: treeTable
426: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
427: treeTable
428: .setGridColor(UIConstants.TABLE_VERTICAL_GRID_COLOR);
429: treeTable
430: .setSelectionBackground(UIConstants.TABLE_SELECTION_BACKGROUND_COLOR);
431: treeTable
432: .setSelectionForeground(UIConstants.TABLE_SELECTION_FOREGROUND_COLOR);
433: treeTable
434: .setShowHorizontalLines(UIConstants.SHOW_TABLE_HORIZONTAL_GRID);
435: treeTable
436: .setShowVerticalLines(UIConstants.SHOW_TABLE_VERTICAL_GRID);
437: treeTable.setRowMargin(UIConstants.TABLE_ROW_MARGIN);
438: treeTable.setRowHeight(UIUtils.getDefaultRowHeight() + 2);
439: treeTable.getInputMap(
440: JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
441: KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
442: "DEFAULT_ACTION"); // NOI18N
443: treeTable.getActionMap().put("DEFAULT_ACTION",
444: new AbstractAction() {
445: public void actionPerformed(ActionEvent e) {
446: performDefaultAction(treePath);
447: }
448: }); // NOI18N
449:
450: // Disable traversing table cells using TAB and Shift+TAB
451: Set keys = new HashSet(
452: treeTable
453: .getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
454: keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
455: treeTable.setFocusTraversalKeys(
456: KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);
457:
458: keys = new HashSet(
459: treeTable
460: .getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
461: keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
462: InputEvent.SHIFT_MASK));
463: treeTable.setFocusTraversalKeys(
464: KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keys);
465:
466: setColumnsData();
467:
468: UIUtils.autoExpandRoot(treeTable.getTree());
469: UIUtils.makeTreeAutoExpandable(treeTable.getTree());
470:
471: treeTable.getSelectionModel().addListSelectionListener(
472: new ListSelectionListener() {
473: public void valueChanged(ListSelectionEvent e) {
474: int selectedRow = treeTable
475: .getSelectedRow();
476:
477: if (selectedRow == -1) {
478: return;
479: }
480:
481: treePath = treeTable.getTree()
482: .getPathForRow(selectedRow);
483: }
484: });
485:
486: treeTable.addKeyListener(new KeyAdapter() {
487: public void keyPressed(KeyEvent e) {
488: if ((e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU)
489: || ((e.getKeyCode() == KeyEvent.VK_F10) && (e
490: .getModifiers() == InputEvent.SHIFT_MASK))) {
491: int selectedRow = treeTable.getSelectedRow();
492:
493: if (selectedRow != -1) {
494: treePath = treeTable.getTree()
495: .getPathForRow(selectedRow);
496:
497: Rectangle cellRect = treeTable.getCellRect(
498: selectedRow, 0, false);
499: popupMenu
500: .show(
501: e.getComponent(),
502: ((cellRect.x + treeTable
503: .getSize().width) > 50) ? 50
504: : 5, cellRect.y);
505: }
506: }
507: }
508: });
509:
510: treeTable.addMouseListener(new MouseAdapter() {
511: public void mousePressed(MouseEvent e) {
512: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
513: treePath = treeTable.getTree().getPathForRow(
514: treeTable.rowAtPoint(e.getPoint()));
515:
516: if (treePath != null) {
517: treeTable.getTree().setSelectionPath(
518: treePath);
519: }
520: }
521: }
522:
523: public void mouseClicked(MouseEvent e) {
524: treePath = treeTable.getTree().getPathForRow(
525: treeTable.rowAtPoint(e.getPoint()));
526:
527: if (treePath == null) {
528: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
529: treeTable.getTree().clearSelection();
530: }
531: } else {
532: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
533: popupMenu.show(e.getComponent(), e.getX(),
534: e.getY());
535: } else if ((e.getModifiers() == InputEvent.BUTTON1_MASK)
536: && (e.getClickCount() == 2)) {
537: if (treeTableModel
538: .isLeaf(treePath.getPath()[treePath
539: .getPath().length - 1])) {
540: performDefaultAction(treePath);
541: }
542: }
543: }
544: }
545: });
546:
547: removeAll();
548: treeTablePanel = new JTreeTablePanel(treeTable);
549: treeTablePanel.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
550: cornerButton);
551: add(treeTablePanel, BorderLayout.CENTER);
552: }
553: }
554: }
|