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.client.ClientUtils;
044: import org.netbeans.lib.profiler.global.CommonConstants;
045: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
046: import org.netbeans.lib.profiler.results.memory.MemoryResultsSnapshot;
047: import org.netbeans.lib.profiler.ui.ResultsPanel;
048: import org.netbeans.lib.profiler.ui.UIUtils;
049: import org.netbeans.lib.profiler.ui.components.FilterComponent;
050: import org.netbeans.lib.profiler.ui.components.JExtendedTable;
051: import org.netbeans.lib.profiler.ui.components.table.CustomBarCellRenderer;
052: import org.netbeans.lib.profiler.ui.components.table.ExtendedTableModel;
053: import org.netbeans.lib.profiler.utils.*;
054: import java.awt.*;
055: import java.awt.event.InputEvent;
056: import java.awt.event.MouseAdapter;
057: import java.awt.event.MouseEvent;
058: import java.awt.event.MouseWheelEvent;
059: import java.awt.event.MouseWheelListener;
060: import java.util.ArrayList;
061: import javax.swing.*;
062: import javax.swing.table.TableCellRenderer;
063:
064: /**
065: * Base abstract class for panels containing memory profiling results in table form.
066: * It consists of a JPanel with embedded JScrollPane, plus a popup menu that is not attached to
067: * anything (that should be done by subclasses). The common functionality provided in it is:
068: * - initialization and generation of displayable data
069: * - reset
070: * - showing results
071: * - getting string title
072: * - sorting results.
073: *
074: * @author Misha Dmitriev
075: * @author Jiri Sedlacek
076: */
077: public abstract class SnapshotMemoryResultsPanel extends ResultsPanel {
078: /* protected MemoryResUserActionsHandler actionsHandler;
079: protected String[] sortedClassNames;
080: protected int[] sortedClassIds;
081: protected int sortBy; // Defines sorting criteria (concrete values provided in subclasses)
082: protected boolean sortOrder; // Defines the sorting order (ascending or descending)
083: protected long totalAllocations;
084: protected int nInfoLines, clickedLine, selectedClassId;
085: protected boolean registeredMouseListenerWithResTable/*, createNamesFromScratch* /;
086: protected CustomBarCellRenderer barRenderer;
087: protected long maxValue; // Used by the bar representation management code
088:
089: protected JScrollPane jScrollPane;
090: protected JExtendedTable resTable;
091: protected ExtendedTableModel resTableModel;
092: protected JButton cornerButton;
093: protected JPopupMenu cornerPopup;
094:
095: protected String[] columnNames;
096: protected Class[] columnTypes;
097: protected String[] columnToolTips;
098: protected TableCellRenderer[] columnRenderers;
099: protected int[] columnWidths;
100:
101: protected int nTrackedItems;
102: protected int nDisplayedItems;
103: protected ArrayList filteredToFullIndexes;
104:
105: protected FilterComponent filterComponent;
106:
107: protected String filterString = "";
108: protected int filterType = CommonConstants.FILTER_CONTAINS;
109: private MemoryResultsSnapshot snapshot;
110: public SnapshotMemoryResultsPanel(MemoryResultsSnapshot snapshot, MemoryResUserActionsHandler actionsHandler) {
111: this.snapshot = snapshot;
112: this.actionsHandler = actionsHandler;
113:
114: filteredToFullIndexes = new ArrayList();
115:
116: cornerPopup = new JPopupMenu();
117: jScrollPane = createScrollPaneVerticalScrollBarAlways();
118: jScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, createHeaderPopupCornerButton(cornerPopup));
119: initFilterPanel();
120: initDataUponResultsFetch ();
121: }
122: private void initFilterPanel() {
123: filterComponent = new FilterComponent();
124: filterComponent.setEmptyFilterText("[Class Name Filter]");
125:
126: filterComponent.addFilterItem(new ImageIcon(filterComponent.getClass().getResource("/org/netbeans/lib/profiler/ui/resources/filterStartsWith.png")), "Starts with", CommonConstants.FILTER_STARTS_WITH);
127: filterComponent.addFilterItem(new ImageIcon(filterComponent.getClass().getResource("/org/netbeans/lib/profiler/ui/resources/filterContains.png")), "Contains", CommonConstants.FILTER_CONTAINS);
128: filterComponent.addFilterItem(new ImageIcon(filterComponent.getClass().getResource("/org/netbeans/lib/profiler/ui/resources/filterEndsWith.png")), "Ends with", CommonConstants.FILTER_ENDS_WITH);
129: filterComponent.addFilterItem(new ImageIcon(filterComponent.getClass().getResource("/org/netbeans/lib/profiler/ui/resources/filterRegExp.png")), "Regular expression", CommonConstants.FILTER_REGEXP);
130: filterComponent.setFilterValues(filterString, filterType);
131: filterComponent.addFilterListener(new FilterComponent.FilterListener() {
132: public void filterChanged() {
133: filterString = filterComponent.getFilterString();
134: filterType = filterComponent.getFilterType();
135: createFilteredIndexes();
136: resTable.invalidate();
137: jScrollPane.revalidate();
138: resTable.repaint();
139: }
140: });
141:
142: add(filterComponent, BorderLayout.SOUTH);
143: }
144: private boolean passesFilter(int idx, String filter) {
145: String value = sortedClassNames[idx];
146: if (filter.equals("")) return true;
147:
148: // Case sensitive comparison:
149: /*switch (type) {
150: case CommonConstants.FILTER_STARTS_WITH:
151: return value.startsWith(filter);
152: case CommonConstants.FILTER_CONTAINS:
153: return value.indexOf(filter) != -1;
154: case CommonConstants.FILTER_ENDS_WITH:
155: return value.endsWith(filter);
156: case CommonConstants.FILTER_EQUALS:
157: return value.equals(filter);
158: case CommonConstants.FILTER_REGEXP:
159: return value.matches(filter);
160: }* /
161:
162: // Case insensitive comparison (except regexp):
163: switch (filterType) {
164: case CommonConstants.FILTER_STARTS_WITH:
165: return value.regionMatches(true, 0, filter, 0, filter.length()); // case insensitive startsWith, optimized
166: case CommonConstants.FILTER_CONTAINS:
167: return value.toLowerCase().indexOf(filter.toLowerCase()) != -1; // case insensitive indexOf, NOT OPTIMIZED!!!
168: case CommonConstants.FILTER_ENDS_WITH:
169: return value.regionMatches(true, value.length() - filter.length(), filter, 0, filter.length()); // case insensitive endsWith, optimized
170: case CommonConstants.FILTER_EQUALS:
171: return value.equalsIgnoreCase(filter); // case insensitive equals
172: case CommonConstants.FILTER_REGEXP:
173: return value.matches(filter); // still case sensitive!
174: }
175:
176: return false;
177: }
178:
179: private boolean passexFilters(int idx) {
180: if (filterType == CommonConstants.FILTER_NONE) return true;
181: String[] filters = FilterComponent.getFilterStrings(filterString);
182: if (filters == null) return true;
183: for (int i = 0; i < filters.length; i++)
184: if (passesFilter(idx, filters[i])) return true;
185: return false;
186: }
187:
188:
189: protected void createFilteredIndexes() {
190: filteredToFullIndexes.clear();
191: for (int i = 0; i < nInfoLines; i++) {
192: if (passexFilters(i) && passesValueFilter (i)) {
193: filteredToFullIndexes.add(new Integer(i));
194: }
195: }
196: nDisplayedItems = filteredToFullIndexes.size();
197: }
198: protected boolean passesValueFilter(int i) {
199: return true;
200: }
201: protected void initDataUponResultsFetch(long maxValueForBarRenderer) {
202: if (barRenderer != null) barRenderer.setMaximum((int)maxValue); // updateState the bar renderer if already exists
203: doCreateClassNamesFromScratch();
204: }
205: public void prepareResults() {
206: final JExtendedTable table = getResultsTable();
207: jScrollPane.setViewportView(table);
208: jScrollPane.getViewport().setBackground(table.getBackground());
209: if (!registeredMouseListenerWithResTable) {
210: jScrollPane.addMouseWheelListener(new MouseWheelListener() {
211: public void mouseWheelMoved(MouseWheelEvent e) {
212: table.mouseWheelMoved(e);
213: }
214: });
215: table.addMouseListener(new MouseAdapter() {
216: public void mousePressed(MouseEvent e) {
217: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
218: int line = table.rowAtPoint(e.getPoint());
219: if (line != -1) table.setRowSelectionInterval(line, line);
220: }
221: }
222:
223: public void mouseClicked(MouseEvent e) {
224: clickedLine = table.rowAtPoint(e.getPoint());
225: if (clickedLine != -1) {
226: //selectedClassId = sortedClassIds[clickedLine];
227: selectedClassId = sortedClassIds[((Integer)filteredToFullIndexes.get(clickedLine)).intValue()];
228: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
229: JPopupMenu popup = getPopupMenu();
230: if (popup != null)
231: popup.show(e.getComponent(), e.getX(), e.getY());
232: } else if ((e.getModifiers() == InputEvent.BUTTON1_MASK) && (e.getClickCount() == 2)) {
233: performDefaultAction (selectedClassId);
234: }
235: }
236: }
237: });
238: registeredMouseListenerWithResTable = true;
239: }
240: }
241: protected abstract JPopupMenu getPopupMenu ();
242: protected abstract void performDefaultAction(int selectedClassId);
243: protected void showSourceForClass(int classId) {
244: if (classId < 0) return;
245: String className = snapshot.getClassName(classId);
246: className = className.replaceAll("\\[", "");
247: className = className.replaceAll("/", ".");
248: if (className.length() == 1) {
249: if (BOOLEAN_CODE.equals (className) ||
250: CHAR_CODE.equals (className) ||
251: BYTE_CODE.equals (className) ||
252: SHORT_CODE.equals (className) ||
253: INT_CODE.equals (className) ||
254: LONG_CODE.equals (className) ||
255: FLOAT_CODE.equals (className) ||
256: DOUBLE_CODE.equals (className))
257: {
258: return; // primitive type
259: }
260: }
261: actionsHandler.showSourceForMethod(className, null, null);
262: }
263: public void reset() {
264: jScrollPane.setViewportView(null);
265: }
266: // ---------------------------------------------------------------------------
267: // Abstract methods that should be implemented by a concrete - data type frame
268:
269: public abstract void fetchResultsFromTargetApp() throws ClientUtils.TargetAppOrVMTerminated;
270: protected abstract JExtendedTable getResultsTable();
271: //----------------------------------------------------------------------------
272: // Sorting results according to different criteria - used in subclasses
273: /**
274: * Sorts the results[] array, aligning secondaryData with it if it's present.
275: * Returns the number of non-zero elements, which may be smaller than the array size.
276: * /
277: protected int sortResults(final int results[],
278: final int secondaryIntData[][], final long secondaryLongData[][], final float secondaryFloatData[][],
279: int off, int len,
280: boolean truncateZeroItems) {
281: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
282: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length : 0;
283: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length : 0;
284: final int nSecFDataArrays = (secondaryFloatData != null) ? secondaryFloatData.length : 0;
285:
286: (new IntSorter(results, off, len) {
287: protected void swap(int a, int b) {
288: if (results[a] != results[b]) {
289: super.swap(a, b);
290: String tmp = sortedClassNames[a];
291: sortedClassNames[a] = sortedClassNames[b];
292: sortedClassNames[b] = tmp;
293: int tmpI = sortedClassIds[a];
294: sortedClassIds[a] = sortedClassIds[b];
295: sortedClassIds[b] = tmpI;
296: for (int i = 0; i < nSecIDataArrays; i++) {
297: tmpI = secondaryIntData[i][a];
298: secondaryIntData[i][a] = secondaryIntData[i][b];
299: secondaryIntData[i][b] = tmpI;
300: }
301: for (int i = 0; i < nSecLDataArrays; i++) {
302: long tmpL = secondaryLongData[i][a];
303: secondaryLongData[i][a] = secondaryLongData[i][b];
304: secondaryLongData[i][b] = tmpL;
305: }
306: for (int i = 0; i < nSecFDataArrays; i++) {
307: float tmpF = secondaryFloatData[i][a];
308: secondaryFloatData[i][a] = secondaryFloatData[i][b];
309: secondaryFloatData[i][b] = tmpF;
310: }
311: }
312: }
313: }).sort(sortOrder);
314: len = off + len; // Note that supplied len may be for a subset of the array, but what's ultimately needed is
315: // the number of non-zero elements for the whole array
316: //if (truncateZeroItems) { // Deal with the fact that some items in the bottom may be just zero
317: // while (len > 0 && results[len - 1] == 0) len--;
318: //}
319: return len;
320: }
321: /**
322: * Sorts the results[] array, aligning secondaryData with it if it's present.
323: * Returns the number of non-zero elements, which may be smaller than the array size.
324: * /
325: protected int sortResults(final long results[],
326: final int secondaryIntData[][], final long secondaryLongData[][], final float secondaryFloatData[][],
327: int off, int len,
328: boolean truncateZeroItems) {
329: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
330: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length : 0;
331: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length : 0;
332: final int nSecFDataArrays = (secondaryFloatData != null) ? secondaryFloatData.length : 0;
333: (new LongSorter(results, off, len) {
334: protected void swap(int a, int b) {
335: if (results[a] != results[b]) {
336: super.swap(a, b);
337: String tmp = sortedClassNames[a];
338: sortedClassNames[a] = sortedClassNames[b];
339: sortedClassNames[b] = tmp;
340: int tmpI = sortedClassIds[a];
341: sortedClassIds[a] = sortedClassIds[b];
342: sortedClassIds[b] = tmpI;
343: for (int i = 0; i < nSecIDataArrays; i++) {
344: tmpI = secondaryIntData[i][a];
345: secondaryIntData[i][a] = secondaryIntData[i][b];
346: secondaryIntData[i][b] = tmpI;
347: }
348: for (int i = 0; i < nSecLDataArrays; i++) {
349: long tmpL = secondaryLongData[i][a];
350: secondaryLongData[i][a] = secondaryLongData[i][b];
351: secondaryLongData[i][b] = tmpL;
352: }
353: for (int i = 0; i < nSecFDataArrays; i++) {
354: float tmpF = secondaryFloatData[i][a];
355: secondaryFloatData[i][a] = secondaryFloatData[i][b];
356: secondaryFloatData[i][b] = tmpF;
357: }
358: }
359: }
360: }).sort(sortOrder);
361: len = off + len; // Note that supplied len may be for a subset of the array, but what's ultimately needed is
362: // the number of non-zero elements for the whole array
363: //if (truncateZeroItems) { // Deal with the fact that some items in the bottom may be just zero
364: // while (len > 0 && results[len - 1] == 0) len--;
365: //}
366: return len;
367: }
368: /**
369: * Sorts the results[] array, aligning secondaryData with it if it's present.
370: * Returns the number of non-zero elements, which may be smaller than the array size.
371: * /
372: protected int sortResults(final float results[],
373: final int secondaryIntData[][], final long secondaryLongData[][],
374: int off, int len,
375: boolean truncateZeroItems) {
376: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
377: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length : 0;
378: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length : 0;
379: (new FloatSorter(results, off, len) {
380: protected void swap(int a, int b) {
381: if (results[a] != results[b]) {
382: super.swap(a, b);
383: String tmp = sortedClassNames[a];
384: sortedClassNames[a] = sortedClassNames[b];
385: sortedClassNames[b] = tmp;
386: int tmpI = sortedClassIds[a];
387: sortedClassIds[a] = sortedClassIds[b];
388: sortedClassIds[b] = tmpI;
389: for (int i = 0; i < nSecIDataArrays; i++) {
390: tmpI = secondaryIntData[i][a];
391: secondaryIntData[i][a] = secondaryIntData[i][b];
392: secondaryIntData[i][b] = tmpI;
393: }
394: for (int i = 0; i < nSecLDataArrays; i++) {
395: long tmpL = secondaryLongData[i][a];
396: secondaryLongData[i][a] = secondaryLongData[i][b];
397: secondaryLongData[i][b] = tmpL;
398: }
399: }
400: }
401: }).sort(sortOrder);
402:
403: len = off + len; // Note that supplied len may be for a subset of the array, but what's ultimately needed is
404: // the number of non-zero elements for the whole array
405: //if (truncateZeroItems) { // Deal with the fact that some items in the bottom may be just zero
406: // while (len > 0 && results[len - 1] == 0) len--;
407: //}
408: return len;
409: }
410: /**
411: * Sorts the results by class name, aligning secondaryIntData and secondaryFloatData with it if it's present.
412: * Additionally, if truncateZeroItems is true, gets items for which secondaryIntData[0][i] is 0, to bottom.
413: * Returns the number of non-zero (as above) elements, which may be smaller than the array size.
414: * /
415: protected int sortResultsByClassName(final int secondaryIntData[][], final long secondaryLongData[][], final float secondaryFloatData[][],
416: int len,
417: boolean truncateZeroItems) {
418: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
419: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length : 0;
420: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length : 0;
421: final int nSecFDataArrays = (secondaryFloatData != null) ? secondaryFloatData.length : 0;
422: if (truncateZeroItems) { // Move zero items to the bottom
423: int head = 0, tail = len - 1;
424: while (head < tail) {
425: while (secondaryIntData[0][tail] == 0 && tail > head) tail--;
426: if (tail <= head) break;
427: while (secondaryIntData[0][head] != 0 && head < tail) head++;
428: if (head >= tail) break;
429: // Now data[headPos] == 0 and data[tailPos] != 0 - swap them
430: String tmpS = sortedClassNames[head];
431: sortedClassNames[head] = sortedClassNames[tail];
432: sortedClassNames[tail] = tmpS;
433: int tmpI = sortedClassIds[head];
434: sortedClassIds[head] = sortedClassIds[tail];
435: sortedClassIds[tail] = tmpI;
436: for (int i = 0; i < nSecIDataArrays; i++) {
437: tmpI = secondaryIntData[i][head];
438: secondaryIntData[i][head] = secondaryIntData[i][tail];
439: secondaryIntData[i][tail] = tmpI;
440: }
441: for (int i = 0; i < nSecLDataArrays; i++) {
442: long tmpL = secondaryLongData[i][head];
443: secondaryLongData[i][head] = secondaryLongData[i][tail];
444: secondaryLongData[i][tail] = tmpL;
445: }
446: for (int i = 0; i < nSecFDataArrays; i++) {
447: float tmpF = secondaryFloatData[i][head];
448: secondaryFloatData[i][head] = secondaryFloatData[i][tail];
449: secondaryFloatData[i][tail] = tmpF;
450: }
451: head++;
452: tail--;
453: }
454: len = head;
455: if (secondaryIntData[0][len] != 0) len++;
456: }
457: (new StringSorter(sortedClassNames, 0, len) {
458: protected void swap(int a, int b) {
459: super.swap(a, b);
460: int tmpI = sortedClassIds[a];
461: sortedClassIds[a] = sortedClassIds[b];
462: sortedClassIds[b] = tmpI;
463: for (int i = 0; i < nSecIDataArrays; i++) {
464: tmpI = secondaryIntData[i][a];
465: secondaryIntData[i][a] = secondaryIntData[i][b];
466: secondaryIntData[i][b] = tmpI;
467: }
468: for (int i = 0; i < nSecLDataArrays; i++) {
469: long tmpL = secondaryLongData[i][a];
470: secondaryLongData[i][a] = secondaryLongData[i][b];
471: secondaryLongData[i][b] = tmpL;
472: }
473: for (int i = 0; i < nSecFDataArrays; i++) {
474: float tmpF = secondaryFloatData[i][a];
475: secondaryFloatData[i][a] = secondaryFloatData[i][b];
476: secondaryFloatData[i][b] = tmpF;
477: }
478: }
479: }).sort(sortOrder);
480: return len;
481: }
482: protected void doCreateClassNamesFromScratch() {
483: if (sortedClassNames == null || sortedClassNames.length < status.classNames.length) {
484: sortedClassNames = null;
485: sortedClassNames = new String[status.classNames.length];
486: sortedClassIds = null;
487: sortedClassIds = new int[status.classNames.length];
488: }
489: for (int i = 0; i < status.classNames.length; i++) {
490: sortedClassNames[i] = StringUtils.userFormClassName(status.classNames[i]);
491: sortedClassIds[i] = i;
492: }
493: }
494: private static final String BOOLEAN_CODE = "Z";
495: private static final String CHAR_CODE = "C";
496: private static final String BYTE_CODE = "B";
497: private static final String SHORT_CODE = "S";
498: private static final String INT_CODE = "I";
499: private static final String LONG_CODE = "J";
500: private static final String FLOAT_CODE = "F";
501: private static final String DOUBLE_CODE = "D";
502:
503: */
504: }
|