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.results.memory.PresoObjAllocCCTNode;
044: import org.netbeans.lib.profiler.ui.ResultsPanel;
045: import org.netbeans.lib.profiler.ui.components.*;
046: import org.netbeans.lib.profiler.ui.components.JTreeTable;
047: import org.netbeans.lib.profiler.ui.components.table.CustomBarCellRenderer;
048: import org.netbeans.lib.profiler.ui.components.table.LabelBracketTableCellRenderer;
049: import org.netbeans.lib.profiler.ui.components.table.LabelTableCellRenderer;
050: import org.netbeans.lib.profiler.ui.components.tree.EnhancedTreeCellRenderer;
051: import org.netbeans.lib.profiler.ui.components.tree.MethodNameTreeCellRenderer;
052: import org.netbeans.lib.profiler.ui.components.treetable.ExtendedTreeTableModel;
053: import org.netbeans.lib.profiler.ui.components.treetable.JTreeTablePanel;
054: import java.awt.*;
055: import java.util.ResourceBundle;
056: import javax.swing.*;
057: import javax.swing.table.TableCellRenderer;
058: import javax.swing.table.TableColumnModel;
059: import javax.swing.tree.TreePath;
060:
061: /**
062: * A panel containing a reverse call graph for all allocations of instances of a given class
063: *
064: * @author Misha Dmitriev
065: * @author Jiri Sedlacek
066: */
067: public abstract class ReverseMemCallGraphPanel extends ResultsPanel {
068: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
069:
070: // -----
071: // I18N String constants
072: private static final ResourceBundle messages = ResourceBundle
073: .getBundle("org.netbeans.lib.profiler.ui.memory.Bundle"); // NOI18N
074: private static final String METHOD_COLUMN_NAME = messages
075: .getString("ReverseMemCallGraphPanel_MethodColumnName"); // NOI18N
076: private static final String LIVE_BYTES_REL_COLUMN_NAME = messages
077: .getString("ReverseMemCallGraphPanel_LiveBytesRelColumnName"); // NOI18N
078: private static final String LIVE_BYTES_COLUMN_NAME = messages
079: .getString("ReverseMemCallGraphPanel_LiveBytesColumnName"); // NOI18N
080: private static final String LIVE_OBJECTS_COLUMN_NAME = messages
081: .getString("ReverseMemCallGraphPanel_LiveObjectsColumnName"); // NOI18N
082: private static final String ALLOC_OBJECTS_COLUMN_NAME = messages
083: .getString("ReverseMemCallGraphPanel_AllocObjectsColumnName"); // NOI18N
084: private static final String AVG_AGE_COLUMN_NAME = messages
085: .getString("ReverseMemCallGraphPanel_AvgAgeColumnName"); // NOI18N
086: private static final String SURVGEN_COLUMN_NAME = messages
087: .getString("ReverseMemCallGraphPanel_SurvGenColumnName"); // NOI18N
088: private static final String METHOD_COLUMN_TOOLTIP = messages
089: .getString("ReverseMemCallGraphPanel_MethodColumnToolTip"); // NOI18N
090: private static final String LIVE_BYTES_REL_COLUMN_TOOLTIP = messages
091: .getString("ReverseMemCallGraphPanel_LiveBytesRelColumnToolTip"); // NOI18N
092: private static final String LIVE_BYTES_COLUMN_TOOLTIP = messages
093: .getString("ReverseMemCallGraphPanel_LiveBytesColumnToolTip"); // NOI18N
094: private static final String LIVE_OBJECTS_COLUMN_TOOLTIP = messages
095: .getString("ReverseMemCallGraphPanel_LiveObjectsColumnToolTip"); // NOI18N
096: private static final String ALLOC_OBJECTS_COLUMN_TOOLTIP = messages
097: .getString("ReverseMemCallGraphPanel_AllocObjectsColumnToolTip"); // NOI18N
098: private static final String AVG_AGE_COLUMN_TOOLTIP = messages
099: .getString("ReverseMemCallGraphPanel_AvgAgeColumnToolTip"); // NOI18N
100: private static final String SURVGEN_COLUMN_TOOLTIP = messages
101: .getString("ReverseMemCallGraphPanel_SurvGenColumnToolTip"); // NOI18N
102: private static final String BYTES_ALLOC_REL_COLUMN_NAME = messages
103: .getString("ReverseMemCallGraphPanel_BytesAllocRelColumnName"); // NOI18N
104: private static final String BYTES_ALLOC_COLUMN_NAME = messages
105: .getString("ReverseMemCallGraphPanel_BytesAllocColumnName"); // NOI18N
106: private static final String OBJECTS_ALLOC_COLUMN_NAME = messages
107: .getString("ReverseMemCallGraphPanel_ObjectsAllocColumnName"); // NOI18N
108: private static final String BYTES_ALLOC_REL_COLUMN_TOOLTIP = messages
109: .getString("ReverseMemCallGraphPanel_BytesAllocRelColumnToolTip"); // NOI18N
110: private static final String BYTES_ALLOC_COLUMN_TOOLTIP = messages
111: .getString("ReverseMemCallGraphPanel_BytesAllocColumnToolTip"); // NOI18N
112: private static final String OBJECTS_ALLOC_COLUMN_TOOLTIP = messages
113: .getString("ReverseMemCallGraphPanel_ObjectsAllocColumnToolTip"); // NOI18N
114: private static final String GO_SOURCE_POPUP_ITEM = messages
115: .getString("ReverseMemCallGraphPanel_GoSourcePopupItem"); // NOI18N
116: // -----
117:
118: //~ Instance fields ----------------------------------------------------------------------------------------------------------
119:
120: protected ExtendedTreeTableModel treeTableModel;
121: protected JButton cornerButton;
122: protected JPopupMenu headerPopup;
123: protected JPopupMenu popupMenu;
124: protected JTreeTable treeTable;
125: protected JTreeTablePanel treeTablePanel;
126: protected MemoryResUserActionsHandler actionsHandler;
127: protected TreePath treePath;
128: protected String[] columnNames;
129: protected TableCellRenderer[] columnRenderers;
130: protected String[] columnToolTips;
131: protected int[] columnWidths;
132: protected boolean extendedResults; // determines Alloc./Liveness results
133: protected int columnCount = 0;
134: protected int minNamesColumnWidth; // minimal width of classnames columns
135: CustomBarCellRenderer customBarCellRenderer;
136: private EnhancedTreeCellRenderer enhancedTreeCellRenderer = new MethodNameTreeCellRenderer();
137: private ImageIcon leafIcon = new ImageIcon(
138: ReverseMemCallGraphPanel.class
139: .getResource("/org/netbeans/lib/profiler/ui/resources/reverseNode.png")); // NOI18N
140: private ImageIcon nodeIcon = new ImageIcon(
141: ReverseMemCallGraphPanel.class
142: .getResource("/org/netbeans/lib/profiler/ui/resources/reverseNode.png")); // NOI18N
143:
144: //~ Constructors -------------------------------------------------------------------------------------------------------------
145:
146: public ReverseMemCallGraphPanel(
147: MemoryResUserActionsHandler actionsHandler,
148: boolean extendedResults) {
149: super ();
150: this .extendedResults = extendedResults;
151: this .actionsHandler = actionsHandler;
152:
153: enhancedTreeCellRenderer.setLeafIcon(leafIcon);
154: enhancedTreeCellRenderer.setClosedIcon(nodeIcon);
155: enhancedTreeCellRenderer.setOpenIcon(nodeIcon);
156:
157: minNamesColumnWidth = getFontMetrics(getFont()).charWidth('W') * 30; // NOI18N
158:
159: headerPopup = new JPopupMenu();
160: cornerButton = createHeaderPopupCornerButton(headerPopup);
161:
162: popupMenu = initPopupMenu();
163:
164: initColumnsData();
165: }
166:
167: //~ Methods ------------------------------------------------------------------------------------------------------------------
168:
169: // --- Find functionality stuff
170: public void setFindString(String findString) {
171: if (treeTable != null) {
172: treeTable.setFindParameters(findString, 0);
173: }
174: }
175:
176: public String getFindString() {
177: if (treeTable == null) {
178: return null;
179: }
180:
181: return treeTable.getFindString();
182: }
183:
184: public boolean isFindStringDefined() {
185: if (treeTable == null) {
186: return false;
187: }
188:
189: return treeTable.isFindStringDefined();
190: }
191:
192: public boolean findFirst() {
193: if (treeTable == null) {
194: return false;
195: }
196:
197: return treeTable.findFirst();
198: }
199:
200: public boolean findNext() {
201: if (treeTable == null) {
202: return false;
203: }
204:
205: return treeTable.findNext();
206: }
207:
208: public boolean findPrevious() {
209: if (treeTable == null) {
210: return false;
211: }
212:
213: return treeTable.findPrevious();
214: }
215:
216: public void requestFocus() {
217: if (treeTable != null) {
218: SwingUtilities.invokeLater(new Runnable() { // must be invoked lazily to override default focus of first component (top-right cornerButton)
219: public void run() {
220: treeTable.requestFocus();
221: }
222: });
223: }
224: }
225:
226: protected void setColumnsData() {
227: int index;
228: TableColumnModel colModel = treeTable.getColumnModel();
229:
230: treeTable.setTreeCellRenderer(enhancedTreeCellRenderer);
231: colModel.getColumn(0).setPreferredWidth(minNamesColumnWidth);
232:
233: for (int i = 0; i < treeTableModel.getColumnCount(); i++) {
234: index = treeTableModel.getRealColumn(i);
235:
236: if (index != 0) {
237: colModel.getColumn(i).setPreferredWidth(
238: columnWidths[index - 1]);
239: colModel.getColumn(i).setCellRenderer(
240: columnRenderers[index]);
241: }
242: }
243: }
244:
245: protected void initColumnSelectorItems() {
246: headerPopup.removeAll();
247:
248: JCheckBoxMenuItem menuItem;
249: boolean columnVisible;
250:
251: for (int i = 0; i < columnCount; i++) {
252: menuItem = new JCheckBoxMenuItem(columnNames[i]);
253: menuItem.setActionCommand(new Integer(i).toString());
254: addMenuItemListener(menuItem);
255:
256: if (treeTable != null) {
257: columnVisible = treeTableModel.isRealColumnVisible(i);
258: menuItem
259: .setState(treeTableModel.isRealColumnVisible(i));
260:
261: if (i == 0) {
262: menuItem.setEnabled(false);
263: }
264: } else {
265: menuItem.setState(true);
266: }
267:
268: headerPopup.add(menuItem);
269: }
270:
271: headerPopup.pack();
272: }
273:
274: protected JPopupMenu initPopupMenu() {
275: JPopupMenu popup = new JPopupMenu();
276: Font boldfont = popup.getFont().deriveFont(Font.BOLD);
277:
278: JMenuItem popupShowSource = new JMenuItem();
279: popupShowSource.setFont(boldfont);
280: popupShowSource.setText(GO_SOURCE_POPUP_ITEM);
281: popupShowSource
282: .addActionListener(new java.awt.event.ActionListener() {
283: public void actionPerformed(
284: java.awt.event.ActionEvent evt) {
285: if (treePath != null) {
286: performDefaultAction(treePath);
287: }
288: }
289: });
290: popup.add(popupShowSource);
291:
292: return popup;
293: }
294:
295: void performDefaultAction(TreePath path) {
296: PresoObjAllocCCTNode node = (PresoObjAllocCCTNode) path
297: .getLastPathComponent();
298: String[] classMethodAndSig = node.getMethodClassNameAndSig();
299: actionsHandler.showSourceForMethod(classMethodAndSig[0],
300: classMethodAndSig[1], classMethodAndSig[2]);
301: }
302:
303: private void addMenuItemListener(JCheckBoxMenuItem menuItem) {
304: menuItem.addActionListener(new java.awt.event.ActionListener() {
305: public void actionPerformed(java.awt.event.ActionEvent e) {
306: boolean sortResults = false;
307: int column = Integer.parseInt(e.getActionCommand());
308: boolean sortOrder = treeTable.getSortingOrder();
309: int sortingColumn = treeTable.getSortingColumn();
310: int realSortingColumn = treeTableModel
311: .getRealColumn(sortingColumn);
312: boolean isColumnVisible = treeTableModel
313: .isRealColumnVisible(column);
314:
315: // Current sorting column is going to be hidden
316: if ((isColumnVisible) && (column == realSortingColumn)) {
317: // Try to set next column as a sortingColumn. If currentSortingColumn is the last column, set previous
318: // column as a sorting Column (one column is always visible).
319: sortingColumn = ((sortingColumn + 1) == treeTableModel
320: .getColumnCount()) ? (sortingColumn - 1)
321: : (sortingColumn + 1);
322: realSortingColumn = treeTableModel
323: .getRealColumn(sortingColumn);
324: sortResults = true;
325: }
326:
327: treeTableModel.setRealColumnVisibility(column,
328: !isColumnVisible);
329: treeTable.createDefaultColumnsFromModel();
330: treeTable.updateTreeTableHeader();
331: sortingColumn = treeTableModel
332: .getVirtualColumn(realSortingColumn);
333:
334: if (sortResults) {
335: sortOrder = treeTableModel
336: .getInitialSorting(sortingColumn);
337: treeTableModel.sortByColumn(sortingColumn,
338: sortOrder);
339: treeTable.updateTreeTable();
340: }
341:
342: treeTable.setSortingColumn(sortingColumn);
343: treeTable.setSortingOrder(sortOrder);
344: treeTable.getTableHeader().repaint();
345: setColumnsData();
346:
347: // TODO [ui-persistence]
348: }
349: });
350: }
351:
352: private void initColumnsData() {
353: if (extendedResults) {
354: columnNames = new String[] { METHOD_COLUMN_NAME,
355: LIVE_BYTES_REL_COLUMN_NAME, LIVE_BYTES_COLUMN_NAME,
356: LIVE_OBJECTS_COLUMN_NAME,
357: ALLOC_OBJECTS_COLUMN_NAME, AVG_AGE_COLUMN_NAME,
358: SURVGEN_COLUMN_NAME };
359: columnToolTips = new String[] { METHOD_COLUMN_TOOLTIP,
360: LIVE_BYTES_REL_COLUMN_TOOLTIP,
361: LIVE_BYTES_COLUMN_TOOLTIP,
362: LIVE_OBJECTS_COLUMN_TOOLTIP,
363: ALLOC_OBJECTS_COLUMN_TOOLTIP,
364: AVG_AGE_COLUMN_TOOLTIP, SURVGEN_COLUMN_TOOLTIP, };
365: } else {
366: columnNames = new String[] { METHOD_COLUMN_NAME,
367: BYTES_ALLOC_REL_COLUMN_NAME,
368: BYTES_ALLOC_COLUMN_NAME, OBJECTS_ALLOC_COLUMN_NAME };
369: columnToolTips = new String[] { METHOD_COLUMN_TOOLTIP,
370: BYTES_ALLOC_REL_COLUMN_TOOLTIP,
371: BYTES_ALLOC_COLUMN_TOOLTIP,
372: OBJECTS_ALLOC_COLUMN_TOOLTIP, };
373: }
374:
375: columnCount = columnNames.length;
376:
377: columnWidths = new int[columnCount - 1]; // Width of the first column fits to width
378: columnRenderers = new TableCellRenderer[columnCount];
379:
380: LabelBracketTableCellRenderer labelBracketTableCellRenderer = new LabelBracketTableCellRenderer(
381: JLabel.TRAILING);
382: LabelTableCellRenderer labelTableCellRenderer = new LabelTableCellRenderer(
383: JLabel.TRAILING);
384:
385: int maxWidth; // initial width of data columns
386:
387: if (extendedResults) {
388: maxWidth = getFontMetrics(getFont()).charWidth('W') * 10; // NOI18N
389: } else {
390: maxWidth = getFontMetrics(getFont()).charWidth('W') * 13; // NOI18N
391: }
392:
393: columnRenderers[0] = null;
394:
395: columnWidths[1 - 1] = maxWidth;
396: columnRenderers[1] = null;
397:
398: for (int i = 2; i < 4; i++) {
399: columnWidths[i - 1] = maxWidth;
400: columnRenderers[i] = labelBracketTableCellRenderer;
401: }
402:
403: for (int i = 4; i < columnNames.length; i++) {
404: columnWidths[i - 1] = maxWidth;
405: columnRenderers[i] = labelTableCellRenderer;
406: }
407: }
408:
409: private void saveColumnsData() {
410: int index;
411: TableColumnModel colModel = treeTable.getColumnModel();
412:
413: for (int i = 0; i < treeTableModel.getColumnCount(); i++) {
414: index = treeTableModel.getRealColumn(i);
415:
416: if (index != 0) {
417: columnWidths[index - 1] = colModel.getColumn(i)
418: .getPreferredWidth();
419: }
420: }
421: }
422: }
|