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.PresoObjAllocCCTNode;
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.JExtendedTable;
049: import org.netbeans.lib.profiler.ui.components.table.ClassNameTableCellRenderer;
050: import org.netbeans.lib.profiler.ui.components.table.CustomBarCellRenderer;
051: import org.netbeans.lib.profiler.ui.components.table.ExtendedTableModel;
052: import org.netbeans.lib.profiler.ui.components.table.LabelBracketTableCellRenderer;
053: import org.netbeans.lib.profiler.ui.components.table.SortableTableModel;
054: import java.awt.KeyboardFocusManager;
055: import java.awt.event.ActionEvent;
056: import java.awt.event.ActionListener;
057: import java.awt.event.InputEvent;
058: import java.awt.event.KeyEvent;
059: import java.util.HashSet;
060: import java.util.ResourceBundle;
061: import java.util.Set;
062: import javax.swing.*;
063: import javax.swing.table.TableCellRenderer;
064: import javax.swing.table.TableColumnModel;
065:
066: /**
067: * This class implements presentation frames for Object Allocation Profiling.
068: *
069: * @author Misha Dmitriev
070: * @author Ian Formanek
071: * @author Jiri Sedlacek
072: */
073: public abstract class AllocResultsPanel extends MemoryResultsPanel {
074: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
075:
076: // -----
077: // I18N String constants
078: private static final ResourceBundle messages = ResourceBundle
079: .getBundle("org.netbeans.lib.profiler.ui.memory.Bundle"); // NOI18N
080: private static final String FILTER_MENU_ITEM_NAME = messages
081: .getString("AllocResultsPanel_FilterMenuItemName"); // NOI18N
082: private static final String CLASS_COLUMN_NAME = messages
083: .getString("AllocResultsPanel_ClassColumnName"); // NOI18N
084: private static final String BYTES_REL_COLUMN_NAME = messages
085: .getString("AllocResultsPanel_BytesRelColumnName"); // NOI18N
086: private static final String BYTES_COLUMN_NAME = messages
087: .getString("AllocResultsPanel_BytesColumnName"); // NOI18N
088: private static final String OBJECTS_COLUMN_NAME = messages
089: .getString("AllocResultsPanel_ObjectsColumnName"); // NOI18N
090: private static final String CLASS_COLUMN_TOOLTIP = messages
091: .getString("AllocResultsPanel_ClassColumnToolTip"); // NOI18N
092: private static final String BYTES_REL_COLUMN_TOOLTIP = messages
093: .getString("AllocResultsPanel_BytesRelColumnToolTip"); // NOI18N
094: private static final String BYTES_COLUMN_TOOLTIP = messages
095: .getString("AllocResultsPanel_BytesColumnToolTip"); // NOI18N
096: private static final String OBJECTS_COLUMN_TOOLTIP = messages
097: .getString("AllocResultsPanel_ObjectsColumnToolTip"); // NOI18N
098: private static final String TABLE_ACCESS_NAME = messages
099: .getString("AllocResultsPanel_TableAccessName"); // NOI18N
100: // -----
101:
102: //~ Instance fields ----------------------------------------------------------------------------------------------------------
103:
104: protected int[] nTotalAllocObjects;
105: protected long[] totalAllocObjectsSize;
106: protected long nTotalBytes;
107: protected long nTotalClasses;
108: private int initialSortingColumn;
109: private int minNamesColumnWidth; // minimal width of classnames columns
110:
111: //~ Constructors -------------------------------------------------------------------------------------------------------------
112:
113: public AllocResultsPanel(MemoryResUserActionsHandler actionsHandler) {
114: super (actionsHandler);
115:
116: setDefaultSorting();
117:
118: initColumnsData();
119: }
120:
121: //~ Methods ------------------------------------------------------------------------------------------------------------------
122:
123: // NOTE: this method only sets sortBy and sortOrder, it doesn't refresh UI!
124: public void setDefaultSorting() {
125: setSorting(1, SortableTableModel.SORT_ORDER_DESC);
126: }
127:
128: // NOTE: this method only sets sortBy and sortOrder, it doesn't refresh UI!
129: public void setSorting(int sColumn, boolean sOrder) {
130: if (sColumn == CommonConstants.SORTING_COLUMN_DEFAULT) {
131: setDefaultSorting();
132: } else {
133: initialSortingColumn = sColumn;
134: sortBy = getSortBy(initialSortingColumn);
135: sortOrder = sOrder;
136: }
137: }
138:
139: public int getSortingColumn() {
140: if (resTableModel == null) {
141: return CommonConstants.SORTING_COLUMN_DEFAULT;
142: }
143:
144: return resTableModel.getRealColumn(resTableModel
145: .getSortingColumn());
146: }
147:
148: public boolean getSortingOrder() {
149: if (resTableModel == null) {
150: return false;
151: }
152:
153: return resTableModel.getSortingOrder();
154: }
155:
156: protected abstract JPopupMenu getPopupMenu();
157:
158: protected CustomBarCellRenderer getBarCellRenderer() {
159: return new CustomBarCellRenderer(0, maxValue);
160: }
161:
162: protected void getResultsSortedByAllocObjNumber() {
163: //
164: getResultsSortedByClassName(true); // Added because of lines toggling when switching between columns 1 and 2.
165: // At first items must be sorted by class names to get defined initial state for
166: // other sorting.
167:
168: int visibleLines = nInfoLines; // Zero or unprofiled classes are filtered, sorting will be applied only to live
169: // data
170: //
171:
172: nInfoLines = sortResults(nTotalAllocObjects, null,
173: new long[][] { totalAllocObjectsSize }, null, 0,
174: visibleLines, false);
175:
176: totalAllocations = 0;
177:
178: for (int i = 0; i < nInfoLines; i++) {
179: totalAllocations += nTotalAllocObjects[i];
180: }
181: }
182:
183: protected void getResultsSortedByAllocObjSize() {
184: //
185: getResultsSortedByClassName(true); // Added because of lines toggling when switching between columns 1 and 2.
186: // At first items must be sorted by class names to get defined initial state
187: // for other sorting.
188:
189: int visibleLines = nInfoLines; // Zero or unprofiled classes are filtered, sorting will be applied only to live
190: // data
191: //
192:
193: nInfoLines = sortResults(totalAllocObjectsSize,
194: new int[][] { nTotalAllocObjects }, null, null, 0,
195: visibleLines, false);
196:
197: totalAllocations = 0;
198:
199: for (int i = 0; i < nInfoLines; i++) {
200: totalAllocations += nTotalAllocObjects[i];
201: }
202: }
203:
204: protected void getResultsSortedByClassName(boolean presortOnly) {
205: nInfoLines = sortResultsByClassName(
206: new int[][] { nTotalAllocObjects },
207: new long[][] { totalAllocObjectsSize }, null,
208: nTrackedItems, truncateZeroItems());
209:
210: if (!presortOnly) {
211: totalAllocations = 0;
212:
213: for (int i = 0; i < nInfoLines; i++) {
214: totalAllocations += nTotalAllocObjects[i];
215: }
216: }
217: }
218:
219: protected JExtendedTable getResultsTable() {
220: sortResults();
221:
222: if (resTable == null) {
223: resTableModel = new ExtendedTableModel(
224: new SortableTableModel() {
225: public String getColumnName(int col) {
226: return columnNames[col];
227: }
228:
229: public int getRowCount() {
230: return nDisplayedItems;
231: }
232:
233: public int getColumnCount() {
234: return columnNames.length;
235: }
236:
237: public Class getColumnClass(int col) {
238: return columnTypes[col];
239: }
240:
241: public Object getValueAt(int row, int col) {
242: return computeValueAt(row, col);
243: }
244:
245: public String getColumnToolTipText(int col) {
246: return columnToolTips[col];
247: }
248:
249: public void sortByColumn(int column,
250: boolean order) {
251: sortBy = getSortBy(column);
252: sortOrder = order;
253:
254: int selectedRow = resTable.getSelectedRow();
255: String selectedRowContents = null;
256:
257: if (selectedRow != -1) {
258: selectedRowContents = (String) resTable
259: .getValueAt(selectedRow, 0);
260: }
261:
262: prepareResults();
263:
264: if (selectedRowContents != null) {
265: resTable.selectRowByContents(
266: selectedRowContents, 0, true);
267: }
268: }
269:
270: /**
271: * @param column The table column index
272: * @return Initial sorting for the specified column - if true, ascending, if false descending
273: */
274: public boolean getInitialSorting(int column) {
275: switch (column) {
276: case 0:
277: return true;
278: default:
279: return false;
280: }
281: }
282: });
283:
284: resTable = new JExtendedTable(resTableModel) {
285: public void doLayout() {
286: int columnsWidthsSum = 0;
287: int realFirstColumn = -1;
288:
289: int index;
290:
291: for (int i = 0; i < resTableModel.getColumnCount(); i++) {
292: index = resTableModel.getRealColumn(i);
293:
294: if (index == 0) {
295: realFirstColumn = i;
296: } else {
297: columnsWidthsSum += getColumnModel()
298: .getColumn(i).getPreferredWidth();
299: }
300: }
301:
302: if (realFirstColumn != -1) {
303: getColumnModel().getColumn(realFirstColumn)
304: .setPreferredWidth(
305: Math.max(getWidth()
306: - columnsWidthsSum,
307: minNamesColumnWidth));
308: }
309:
310: super .doLayout();
311: };
312: };
313: resTable.getAccessibleContext().setAccessibleName(
314: TABLE_ACCESS_NAME);
315:
316: resTableModel.setTable(resTable);
317: resTableModel.setInitialSorting(initialSortingColumn,
318: sortOrder);
319: resTable.setRowSelectionAllowed(true);
320: resTable
321: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
322: resTable
323: .setGridColor(UIConstants.TABLE_VERTICAL_GRID_COLOR);
324: resTable
325: .setSelectionBackground(UIConstants.TABLE_SELECTION_BACKGROUND_COLOR);
326: resTable
327: .setSelectionForeground(UIConstants.TABLE_SELECTION_FOREGROUND_COLOR);
328: resTable
329: .setShowHorizontalLines(UIConstants.SHOW_TABLE_HORIZONTAL_GRID);
330: resTable
331: .setShowVerticalLines(UIConstants.SHOW_TABLE_VERTICAL_GRID);
332: resTable.setRowMargin(UIConstants.TABLE_ROW_MARGIN);
333: resTable.setRowHeight(UIUtils.getDefaultRowHeight() + 2);
334:
335: // Disable traversing table cells using TAB and Shift+TAB
336: Set keys = new HashSet(
337: resTable
338: .getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
339: keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
340: resTable.setFocusTraversalKeys(
341: KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);
342:
343: keys = new HashSet(
344: resTable
345: .getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
346: keys.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
347: InputEvent.SHIFT_MASK));
348: resTable.setFocusTraversalKeys(
349: KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, keys);
350:
351: setColumnsData();
352: }
353:
354: return resTable;
355: }
356:
357: protected Object computeValueAt(int row, int col) {
358: int index = ((Integer) filteredToFullIndexes.get(row))
359: .intValue();
360:
361: switch (col) {
362: case 0:
363: return sortedClassNames[index];
364: case 1:
365: return new Long(totalAllocObjectsSize[index]);
366: case 2:
367: return intFormat.format(totalAllocObjectsSize[index])
368: + " B (" // NOI18N
369: + ((nTotalBytes == 0) ? "-%" : // NOI18N
370: percentFormat
371: .format((double) totalAllocObjectsSize[index]
372: / (double) nTotalBytes))
373: + ")"; // NOI18N
374: case 3:
375: return intFormat.format(nTotalAllocObjects[index])
376: + " (" // NOI18N
377: + ((nTotalClasses == 0) ? "-%" : // NOI18N
378: percentFormat
379: .format((double) nTotalAllocObjects[index]
380: / (double) nTotalClasses))
381: + ")"; // NOI18N
382: default:
383: return null;
384: }
385: }
386:
387: protected void initColumnSelectorItems() {
388: headerPopup.removeAll();
389:
390: JCheckBoxMenuItem menuItem;
391:
392: for (int i = 0; i < columnNames.length; i++) {
393: menuItem = new JCheckBoxMenuItem(columnNames[i]);
394: menuItem.setActionCommand(new Integer(i).toString());
395: addMenuItemListener(menuItem);
396:
397: if (resTable != null) {
398: menuItem.setState(resTableModel.isRealColumnVisible(i));
399:
400: if (i == 0) {
401: menuItem.setEnabled(false);
402: }
403: } else {
404: menuItem.setState(true);
405: }
406:
407: headerPopup.add(menuItem);
408: }
409:
410: headerPopup.addSeparator();
411:
412: JCheckBoxMenuItem filterMenuItem = new JCheckBoxMenuItem(
413: FILTER_MENU_ITEM_NAME);
414: filterMenuItem.setActionCommand("Filter"); // NOI18N
415: addMenuItemListener(filterMenuItem);
416:
417: if (filterComponent == null) {
418: filterMenuItem.setState(true);
419: } else {
420: filterMenuItem.setState(filterComponent.isVisible());
421: }
422:
423: headerPopup.add(filterMenuItem);
424: headerPopup.pack();
425: }
426:
427: protected void initColumnsData() {
428: int maxWidth = getFontMetrics(getFont()).charWidth('W') * 13; // NOI18N // initial width of data columns
429: minNamesColumnWidth = getFontMetrics(getFont()).charWidth('W') * 30; // NOI18N
430:
431: ClassNameTableCellRenderer classNameTableCellRenderer = new ClassNameTableCellRenderer();
432: LabelBracketTableCellRenderer labelBracketTableCellRenderer = new LabelBracketTableCellRenderer(
433: JLabel.TRAILING);
434:
435: columnNames = new String[] { CLASS_COLUMN_NAME,
436: BYTES_REL_COLUMN_NAME, BYTES_COLUMN_NAME,
437: OBJECTS_COLUMN_NAME };
438: columnToolTips = new String[] { CLASS_COLUMN_TOOLTIP,
439: BYTES_REL_COLUMN_TOOLTIP, BYTES_COLUMN_TOOLTIP,
440: OBJECTS_COLUMN_TOOLTIP };
441: columnTypes = new Class[] { String.class, Number.class,
442: String.class, String.class };
443: columnRenderers = new TableCellRenderer[] {
444: classNameTableCellRenderer, null,
445: labelBracketTableCellRenderer,
446: labelBracketTableCellRenderer };
447: columnWidths = new int[] { maxWidth, maxWidth, maxWidth };
448: }
449:
450: protected boolean passesValueFilter(int i) {
451: return ((((double) totalAllocObjectsSize[i] / (double) nTotalBytes) * 100f) >= valueFilterValue);
452: }
453:
454: protected void performDefaultAction(int classId) {
455: showSourceForClass(classId);
456: }
457:
458: private void setColumnsData() {
459: barRenderer = getBarCellRenderer();
460:
461: TableColumnModel colModel = resTable.getColumnModel();
462: colModel.getColumn(0).setPreferredWidth(minNamesColumnWidth);
463:
464: int index;
465:
466: for (int i = 0; i < colModel.getColumnCount(); i++) {
467: index = resTableModel.getRealColumn(i);
468:
469: if (index == 0) {
470: colModel.getColumn(i).setPreferredWidth(
471: minNamesColumnWidth);
472: } else {
473: colModel.getColumn(i).setPreferredWidth(
474: columnWidths[index - 1]);
475: }
476:
477: if (index == 1) {
478: colModel.getColumn(i).setCellRenderer(barRenderer);
479: } else {
480: colModel.getColumn(i).setCellRenderer(
481: columnRenderers[index]);
482: }
483: }
484: }
485:
486: private int getSortBy(int column) {
487: switch (column) {
488: case 0:
489: return PresoObjAllocCCTNode.SORT_BY_NAME;
490: case 1:
491: return PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_SIZE;
492: case 2:
493: return PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_SIZE;
494: case 3:
495: return PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_NUMBER;
496: }
497:
498: return PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_SIZE;
499: }
500:
501: private void addMenuItemListener(JCheckBoxMenuItem menuItem) {
502: menuItem.addActionListener(new ActionListener() {
503: public void actionPerformed(ActionEvent e) {
504: if (e.getActionCommand().equals("Filter")) { // NOI18N
505: filterComponent.setVisible(!filterComponent
506: .isVisible());
507:
508: // TODO [ui-persistence]
509: return;
510: }
511:
512: saveColumnsData();
513:
514: boolean sortResults = false;
515: int column = Integer.parseInt(e.getActionCommand());
516: int sortingColumn = resTableModel.getSortingColumn();
517: int realSortingColumn = resTableModel
518: .getRealColumn(sortingColumn);
519: boolean isColumnVisible = resTableModel
520: .isRealColumnVisible(column);
521:
522: // Current sorting column is going to be hidden
523: if ((isColumnVisible) && (column == realSortingColumn)) {
524: // Try to set next column as a sortingColumn. If currentSortingColumn is the last column, set previous
525: // column asa sorting Column (one column is always visible).
526: sortingColumn = ((sortingColumn + 1) == resTableModel
527: .getColumnCount()) ? (sortingColumn - 1)
528: : (sortingColumn + 1);
529: realSortingColumn = resTableModel
530: .getRealColumn(sortingColumn);
531: sortResults = true;
532: }
533:
534: resTableModel.setRealColumnVisibility(column,
535: !isColumnVisible);
536: resTable.createDefaultColumnsFromModel();
537: resTableModel.setTable(resTable);
538: sortingColumn = resTableModel
539: .getVirtualColumn(realSortingColumn);
540:
541: if (sortResults) {
542: sortOrder = resTableModel
543: .getInitialSorting(sortingColumn);
544: sortBy = getSortBy(realSortingColumn);
545: sortResults();
546: resTable.repaint();
547: }
548:
549: resTableModel.setInitialSorting(sortingColumn,
550: sortOrder);
551: resTable.getTableHeader().repaint();
552:
553: setColumnsData();
554:
555: // TODO [ui-persistence]
556: }
557: });
558: }
559:
560: private void saveColumnsData() {
561: int index;
562: TableColumnModel colModel = resTable.getColumnModel();
563:
564: for (int i = 0; i < resTableModel.getColumnCount(); i++) {
565: index = resTableModel.getRealColumn(i);
566:
567: if (index != 0) {
568: columnWidths[index - 1] = colModel.getColumn(i)
569: .getPreferredWidth();
570: }
571: }
572: }
573:
574: private void sortResults() {
575: // This will sort results and produce sortedClassNames and sortedClassIds
576: switch (sortBy) {
577: case PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_SIZE:
578: getResultsSortedByAllocObjSize();
579:
580: break;
581: case PresoObjAllocCCTNode.SORT_BY_ALLOC_OBJ_NUMBER:
582: getResultsSortedByAllocObjNumber();
583:
584: break;
585: case PresoObjAllocCCTNode.SORT_BY_NAME:
586: getResultsSortedByClassName(false);
587:
588: break;
589: }
590:
591: createFilteredIndexes();
592: }
593: }
|