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.ui.ResultsPanel;
045: import org.netbeans.lib.profiler.ui.UIUtils;
046: import org.netbeans.lib.profiler.ui.components.FilterComponent;
047: import org.netbeans.lib.profiler.ui.components.JExtendedTable;
048: import org.netbeans.lib.profiler.ui.components.table.CustomBarCellRenderer;
049: import org.netbeans.lib.profiler.ui.components.table.ExtendedTableModel;
050: import org.netbeans.lib.profiler.utils.*;
051: import java.awt.*;
052: import java.awt.event.*;
053: import java.awt.image.BufferedImage;
054: import java.util.ArrayList;
055: import java.util.ResourceBundle;
056: import javax.swing.*;
057: import javax.swing.event.ListSelectionEvent;
058: import javax.swing.event.ListSelectionListener;
059: import javax.swing.table.TableCellRenderer;
060:
061: /**
062: * Base abstract class for panels containing memory profiling results in table form.
063: * It consists of a JPanel with embedded JScrollPane, plus a popup menu that is not attached to
064: * anything (that should be done by subclasses). The common functionality provided in it is:
065: * - initialization and generation of displayable data
066: * - reset
067: * - showing results
068: * - getting string title
069: * - sorting results.
070: *
071: * @author Misha Dmitriev
072: * @author Jiri Sedlacek
073: * @author Ian Formanek
074: */
075: public abstract class MemoryResultsPanel extends ResultsPanel {
076: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
077:
078: // -----
079: // I18N String constants
080: private static final ResourceBundle messages = ResourceBundle
081: .getBundle("org.netbeans.lib.profiler.ui.memory.Bundle"); // NOI18N
082: private static final String DEFAULT_FILTER_HINT = messages
083: .getString("MemoryResultsPanel_DefaultFilterHint"); // NOI18N
084: private static final String STARTS_WITH_FILTER_NAME = messages
085: .getString("MemoryResultsPanel_StartsWithFilterName"); // NOI18N
086: private static final String CONTAINS_FILTER_NAME = messages
087: .getString("MemoryResultsPanel_ContainsFilterName"); // NOI18N
088: private static final String ENDS_WITH_FILTER_NAME = messages
089: .getString("MemoryResultsPanel_EndsWithFilterName"); // NOI18N
090: private static final String REGEXP_FILTER_NAME = messages
091: .getString("MemoryResultsPanel_RegExpFilterName"); // NOI18N
092: // -----
093:
094: //~ Instance fields ----------------------------------------------------------------------------------------------------------
095:
096: protected ArrayList filteredToFullIndexes;
097: protected CustomBarCellRenderer barRenderer;
098: protected ExtendedTableModel resTableModel;
099: protected FilterComponent filterComponent;
100: protected JButton cornerButton;
101: protected JExtendedTable resTable;
102: protected JPopupMenu headerPopup;
103: protected JScrollPane jScrollPane;
104: protected MemoryResUserActionsHandler actionsHandler;
105: protected String filterString = ""; // NOI18N
106: protected String[] columnNames;
107: protected TableCellRenderer[] columnRenderers;
108: protected String[] columnToolTips;
109: protected Class[] columnTypes;
110: protected int[] columnWidths;
111: protected int[] sortedClassIds; // this maps row idx to classId (classId = original, before-sort, row index)
112: protected String[] sortedClassNames; // this is effectively a copy of the class names contained in profilingSessionStatus
113: // or MemoryResultsSnapshot, in user-level format and sorted according to current
114: // sorting criteria
115: protected boolean registeredMouseListenerWithResTable;
116: protected boolean sortOrder; // Defines the sorting order (ascending or descending)
117: protected double valueFilterValue = 0.0d;
118: protected int clickedLine;
119: protected int filterType = CommonConstants.FILTER_CONTAINS;
120: protected int nDisplayedItems;
121: protected int nInfoLines;
122: protected int nTrackedItems;
123: protected int selectedClassId;
124: protected int sortBy; // Defines sorting criteria (concrete values provided in subclasses)
125: protected long maxValue; // Used by the bar representation management code
126: protected long totalAllocations;
127:
128: //~ Constructors -------------------------------------------------------------------------------------------------------------
129:
130: public MemoryResultsPanel(MemoryResUserActionsHandler actionsHandler) {
131: this .actionsHandler = actionsHandler;
132:
133: filteredToFullIndexes = new ArrayList();
134:
135: headerPopup = new JPopupMenu();
136: jScrollPane = createScrollPaneVerticalScrollBarAlways();
137: jScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
138: createHeaderPopupCornerButton(headerPopup));
139:
140: initFilterPanel();
141: }
142:
143: //~ Methods ------------------------------------------------------------------------------------------------------------------
144:
145: public BufferedImage getCurrentViewScreenshot(
146: boolean onlyVisibleArea) {
147: if (resTable == null) {
148: return null;
149: }
150:
151: if (onlyVisibleArea) {
152: return UIUtils.createScreenshot(jScrollPane);
153: } else {
154: return UIUtils.createScreenshot(resTable);
155: }
156: }
157:
158: // --- Find functionality stuff
159: public void setFindString(String findString) {
160: resTable.setFindParameters(findString, 0);
161: }
162:
163: public String getFindString() {
164: return resTable.getFindString();
165: }
166:
167: public boolean isFindStringDefined() {
168: return resTable.isFindStringDefined();
169: }
170:
171: public boolean findFirst() {
172: return resTable.findFirst();
173: }
174:
175: public boolean findNext() {
176: return resTable.findNext();
177: }
178:
179: public boolean findPrevious() {
180: return resTable.findPrevious();
181: }
182:
183: public boolean fitsVisibleArea() {
184: return !jScrollPane.getVerticalScrollBar().isEnabled();
185: }
186:
187: public void prepareResults() {
188: final JExtendedTable table = getResultsTable();
189: resTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
190: .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
191: "DEFAULT_ACTION"); // NOI18N
192: resTable.getActionMap().put("DEFAULT_ACTION",
193: new AbstractAction() {
194: public void actionPerformed(ActionEvent e) {
195: performDefaultAction(selectedClassId);
196: }
197: }); // NOI18N
198:
199: jScrollPane.setViewportView(table);
200: jScrollPane.getViewport().setBackground(table.getBackground());
201:
202: if (!registeredMouseListenerWithResTable) {
203: jScrollPane.addMouseWheelListener(new MouseWheelListener() {
204: public void mouseWheelMoved(MouseWheelEvent e) {
205: table.mouseWheelMoved(e);
206: }
207: });
208:
209: table.getSelectionModel().addListSelectionListener(
210: new ListSelectionListener() {
211: public void valueChanged(ListSelectionEvent e) {
212: int selectedRow = table.getSelectedRow();
213:
214: if (selectedRow == -1) {
215: return;
216: }
217:
218: selectedClassId = sortedClassIds[((Integer) filteredToFullIndexes
219: .get(selectedRow)).intValue()];
220: }
221: });
222:
223: table.addKeyListener(new KeyAdapter() {
224: public void keyPressed(KeyEvent e) {
225: if ((e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU)
226: || ((e.getKeyCode() == KeyEvent.VK_F10) && (e
227: .getModifiers() == InputEvent.SHIFT_MASK))) {
228: int selectedRow = table.getSelectedRow();
229:
230: if (selectedRow != -1) {
231: selectedClassId = sortedClassIds[((Integer) filteredToFullIndexes
232: .get(selectedRow)).intValue()];
233:
234: Rectangle cellRect = table.getCellRect(
235: selectedRow, 0, false);
236: JPopupMenu popup = getPopupMenu();
237:
238: if (popup != null) {
239: popup
240: .show(
241: e.getComponent(),
242: ((cellRect.x + table
243: .getSize().width) > 50) ? 50
244: : 5, cellRect.y);
245: }
246: }
247: }
248: }
249: });
250:
251: table.addMouseListener(new MouseAdapter() {
252: public void mousePressed(MouseEvent e) {
253: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
254: int line = table.rowAtPoint(e.getPoint());
255:
256: if (line != -1) {
257: table.setRowSelectionInterval(line, line);
258: }
259: }
260: }
261:
262: public void mouseClicked(MouseEvent e) {
263: clickedLine = table.rowAtPoint(e.getPoint());
264:
265: if (clickedLine != -1) {
266: //selectedClassId = sortedClassIds[clickedLine];
267: selectedClassId = sortedClassIds[((Integer) filteredToFullIndexes
268: .get(clickedLine)).intValue()];
269:
270: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
271: JPopupMenu popup = getPopupMenu();
272:
273: if (popup != null) {
274: popup.show(e.getComponent(), e.getX(),
275: e.getY());
276: }
277: } else if ((e.getModifiers() == InputEvent.BUTTON1_MASK)
278: && (e.getClickCount() == 2)) {
279: performDefaultAction(selectedClassId);
280: }
281: }
282: }
283: });
284: registeredMouseListenerWithResTable = true;
285: }
286: }
287:
288: public void requestFocus() {
289: if (resTable != null) {
290: SwingUtilities.invokeLater(new Runnable() { // must be invoked lazily to override default focus of first component (top-right cornerButton)
291: public void run() {
292: resTable.requestFocus();
293: }
294: });
295: }
296: }
297:
298: public void reset() {
299: jScrollPane.setViewportView(null);
300: }
301:
302: public void updateValueFilter(double value) {
303: valueFilterValue = value / 3f; // maximum 33.3%
304: createFilteredIndexes();
305: resTable.invalidate();
306: jScrollPane.revalidate();
307: resTable.repaint();
308: }
309:
310: protected abstract String getClassName(int classid);
311:
312: protected abstract String[] getClassNames();
313:
314: protected abstract JPopupMenu getPopupMenu();
315:
316: // ---------------------------------------------------------------------------
317: // Abstract methods that should be implemented by a concrete - data type frame
318: protected abstract JExtendedTable getResultsTable();
319:
320: protected void createFilteredIndexes() {
321: filteredToFullIndexes.clear();
322:
323: for (int i = 0; i < nInfoLines; i++) {
324: if (passesFilters(i) && passesValueFilter(i)) {
325: filteredToFullIndexes.add(new Integer(i));
326: }
327: }
328:
329: nDisplayedItems = filteredToFullIndexes.size();
330: }
331:
332: protected final void doCreateClassNamesFromScratch() {
333: String[] classNames = getClassNames();
334:
335: if ((sortedClassNames == null)
336: || (sortedClassNames.length < classNames.length)) {
337: sortedClassNames = null;
338: sortedClassNames = new String[classNames.length];
339: sortedClassIds = null;
340: sortedClassIds = new int[classNames.length];
341: }
342:
343: for (int i = 0; i < classNames.length; i++) {
344: sortedClassNames[i] = StringUtils
345: .userFormClassName(classNames[i]);
346: sortedClassIds[i] = i;
347: }
348: }
349:
350: protected void initDataUponResultsFetch() {
351: if (barRenderer != null) {
352: barRenderer.setMaximum(maxValue); // updateState the bar renderer if already exists
353: }
354:
355: doCreateClassNamesFromScratch();
356: }
357:
358: protected boolean passesValueFilter(int i) {
359: return true;
360: }
361:
362: protected abstract void performDefaultAction(int selectedClassId);
363:
364: protected void showSourceForClass(int classId) {
365: if (classId < 0) {
366: return;
367: }
368:
369: String className = getClassName(classId);
370: className = className.replaceAll("\\[", ""); // NOI18N
371: className = className.replaceAll("/", "."); // NOI18N
372:
373: actionsHandler.showSourceForMethod(className, null, null);
374: }
375:
376: //----------------------------------------------------------------------------
377: // Sorting results according to different criteria - used in subclasses
378:
379: /**
380: * Sorts the results[] array, aligning secondaryData with it if it's present.
381: * Returns the number of non-zero elements, which may be smaller than the array size.
382: */
383: protected int sortResults(final int[] results,
384: final int[][] secondaryIntData,
385: final long[][] secondaryLongData,
386: final float[][] secondaryFloatData, int off, int len,
387: boolean truncateZeroItems) {
388: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
389: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length
390: : 0;
391: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length
392: : 0;
393: final int nSecFDataArrays = (secondaryFloatData != null) ? secondaryFloatData.length
394: : 0;
395:
396: (new IntSorter(results, off, len) {
397: protected void swap(int a, int b) {
398: if (results[a] != results[b]) {
399: super .swap(a, b);
400:
401: String tmp = sortedClassNames[a];
402: sortedClassNames[a] = sortedClassNames[b];
403: sortedClassNames[b] = tmp;
404:
405: int tmpI = sortedClassIds[a];
406: sortedClassIds[a] = sortedClassIds[b];
407: sortedClassIds[b] = tmpI;
408:
409: for (int i = 0; i < nSecIDataArrays; i++) {
410: tmpI = secondaryIntData[i][a];
411: secondaryIntData[i][a] = secondaryIntData[i][b];
412: secondaryIntData[i][b] = tmpI;
413: }
414:
415: for (int i = 0; i < nSecLDataArrays; i++) {
416: long tmpL = secondaryLongData[i][a];
417: secondaryLongData[i][a] = secondaryLongData[i][b];
418: secondaryLongData[i][b] = tmpL;
419: }
420:
421: for (int i = 0; i < nSecFDataArrays; i++) {
422: float tmpF = secondaryFloatData[i][a];
423: secondaryFloatData[i][a] = secondaryFloatData[i][b];
424: secondaryFloatData[i][b] = tmpF;
425: }
426: }
427: }
428: }).sort(sortOrder);
429:
430: len = off + len; // Note that supplied len may be for a subset of the array, but what's ultimately needed is
431: // the number of non-zero elements for the whole array
432: //if (truncateZeroItems) { // Deal with the fact that some items in the bottom may be just zero
433: // while (len > 0 && results[len - 1] == 0) len--;
434: //}
435:
436: return len;
437: }
438:
439: /**
440: * Sorts the results[] array, aligning secondaryData with it if it's present.
441: * Returns the number of non-zero elements, which may be smaller than the array size.
442: */
443: protected int sortResults(final long[] results,
444: final int[][] secondaryIntData,
445: final long[][] secondaryLongData,
446: final float[][] secondaryFloatData, int off, int len,
447: boolean truncateZeroItems) {
448: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
449: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length
450: : 0;
451: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length
452: : 0;
453: final int nSecFDataArrays = (secondaryFloatData != null) ? secondaryFloatData.length
454: : 0;
455:
456: (new LongSorter(results, off, len) {
457: protected void swap(int a, int b) {
458: if (results[a] != results[b]) {
459: super .swap(a, b);
460:
461: String tmp = sortedClassNames[a];
462: sortedClassNames[a] = sortedClassNames[b];
463: sortedClassNames[b] = tmp;
464:
465: int tmpI = sortedClassIds[a];
466: sortedClassIds[a] = sortedClassIds[b];
467: sortedClassIds[b] = tmpI;
468:
469: for (int i = 0; i < nSecIDataArrays; i++) {
470: tmpI = secondaryIntData[i][a];
471: secondaryIntData[i][a] = secondaryIntData[i][b];
472: secondaryIntData[i][b] = tmpI;
473: }
474:
475: for (int i = 0; i < nSecLDataArrays; i++) {
476: long tmpL = secondaryLongData[i][a];
477: secondaryLongData[i][a] = secondaryLongData[i][b];
478: secondaryLongData[i][b] = tmpL;
479: }
480:
481: for (int i = 0; i < nSecFDataArrays; i++) {
482: float tmpF = secondaryFloatData[i][a];
483: secondaryFloatData[i][a] = secondaryFloatData[i][b];
484: secondaryFloatData[i][b] = tmpF;
485: }
486: }
487: }
488: }).sort(sortOrder);
489:
490: len = off + len; // Note that supplied len may be for a subset of the array, but what's ultimately needed is
491: // the number of non-zero elements for the whole array
492: //if (truncateZeroItems) { // Deal with the fact that some items in the bottom may be just zero
493: // while (len > 0 && results[len - 1] == 0) len--;
494: //}
495:
496: return len;
497: }
498:
499: /**
500: * Sorts the results[] array, aligning secondaryData with it if it's present.
501: * Returns the number of non-zero elements, which may be smaller than the array size.
502: */
503: protected int sortResults(final float[] results,
504: final int[][] secondaryIntData,
505: final long[][] secondaryLongData, int off, int len,
506: boolean truncateZeroItems) {
507: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
508: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length
509: : 0;
510: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length
511: : 0;
512:
513: (new FloatSorter(results, off, len) {
514: protected void swap(int a, int b) {
515: if (results[a] != results[b]) {
516: super .swap(a, b);
517:
518: String tmp = sortedClassNames[a];
519: sortedClassNames[a] = sortedClassNames[b];
520: sortedClassNames[b] = tmp;
521:
522: int tmpI = sortedClassIds[a];
523: sortedClassIds[a] = sortedClassIds[b];
524: sortedClassIds[b] = tmpI;
525:
526: for (int i = 0; i < nSecIDataArrays; i++) {
527: tmpI = secondaryIntData[i][a];
528: secondaryIntData[i][a] = secondaryIntData[i][b];
529: secondaryIntData[i][b] = tmpI;
530: }
531:
532: for (int i = 0; i < nSecLDataArrays; i++) {
533: long tmpL = secondaryLongData[i][a];
534: secondaryLongData[i][a] = secondaryLongData[i][b];
535: secondaryLongData[i][b] = tmpL;
536: }
537: }
538: }
539: }).sort(sortOrder);
540:
541: len = off + len; // Note that supplied len may be for a subset of the array, but what's ultimately needed is
542: // the number of non-zero elements for the whole array
543: //if (truncateZeroItems) { // Deal with the fact that some items in the bottom may be just zero
544: // while (len > 0 && results[len - 1] == 0) len--;
545: //}
546:
547: return len;
548: }
549:
550: /**
551: * Sorts the results by class name, aligning secondaryIntData and secondaryFloatData with it if it's present.
552: * Additionally, if truncateZeroItems is true, gets items for which secondaryIntData[0][i] is 0, to bottom.
553: * Returns the number of non-zero (as above) elements, which may be smaller than the array size.
554: */
555: protected int sortResultsByClassName(
556: final int[][] secondaryIntData,
557: final long[][] secondaryLongData,
558: final float[][] secondaryFloatData, int len,
559: boolean truncateZeroItems) {
560: if (len == 0) {
561: return 0; // no processing for zero-length
562: //if (createNamesFromScratch) doCreateClassNamesFromScratch();
563: }
564:
565: final int nSecIDataArrays = (secondaryIntData != null) ? secondaryIntData.length
566: : 0;
567: final int nSecLDataArrays = (secondaryLongData != null) ? secondaryLongData.length
568: : 0;
569: final int nSecFDataArrays = (secondaryFloatData != null) ? secondaryFloatData.length
570: : 0;
571:
572: if (truncateZeroItems) { // Move zero items to the bottom
573:
574: int head = 0;
575: int tail = len - 1;
576:
577: while (head < tail) {
578: while ((secondaryIntData[0][tail] == 0)
579: && (tail > head)) {
580: tail--;
581: }
582:
583: if (tail <= head) {
584: break;
585: }
586:
587: while ((secondaryIntData[0][head] != 0)
588: && (head < tail)) {
589: head++;
590: }
591:
592: if (head >= tail) {
593: break;
594: }
595:
596: // Now data[headPos] == 0 and data[tailPos] != 0 - swap them
597: String tmpS = sortedClassNames[head];
598: sortedClassNames[head] = sortedClassNames[tail];
599: sortedClassNames[tail] = tmpS;
600:
601: int tmpI = sortedClassIds[head];
602: sortedClassIds[head] = sortedClassIds[tail];
603: sortedClassIds[tail] = tmpI;
604:
605: for (int i = 0; i < nSecIDataArrays; i++) {
606: tmpI = secondaryIntData[i][head];
607: secondaryIntData[i][head] = secondaryIntData[i][tail];
608: secondaryIntData[i][tail] = tmpI;
609: }
610:
611: for (int i = 0; i < nSecLDataArrays; i++) {
612: long tmpL = secondaryLongData[i][head];
613: secondaryLongData[i][head] = secondaryLongData[i][tail];
614: secondaryLongData[i][tail] = tmpL;
615: }
616:
617: for (int i = 0; i < nSecFDataArrays; i++) {
618: float tmpF = secondaryFloatData[i][head];
619: secondaryFloatData[i][head] = secondaryFloatData[i][tail];
620: secondaryFloatData[i][tail] = tmpF;
621: }
622:
623: head++;
624: tail--;
625: }
626:
627: len = head;
628:
629: if ((secondaryIntData == null)
630: || (secondaryIntData[0] == null)
631: || (secondaryIntData.length == 0)
632: || (secondaryIntData[0].length == 0)) {
633: System.out.println("break me"); // NOI18N
634: }
635:
636: if (secondaryIntData[0][len] != 0) {
637: len++;
638: }
639: }
640:
641: (new StringSorter(sortedClassNames, 0, len) {
642: protected void swap(int a, int b) {
643: super .swap(a, b);
644:
645: int tmpI = sortedClassIds[a];
646: sortedClassIds[a] = sortedClassIds[b];
647: sortedClassIds[b] = tmpI;
648:
649: for (int i = 0; i < nSecIDataArrays; i++) {
650: tmpI = secondaryIntData[i][a];
651: secondaryIntData[i][a] = secondaryIntData[i][b];
652: secondaryIntData[i][b] = tmpI;
653: }
654:
655: for (int i = 0; i < nSecLDataArrays; i++) {
656: long tmpL = secondaryLongData[i][a];
657: secondaryLongData[i][a] = secondaryLongData[i][b];
658: secondaryLongData[i][b] = tmpL;
659: }
660:
661: for (int i = 0; i < nSecFDataArrays; i++) {
662: float tmpF = secondaryFloatData[i][a];
663: secondaryFloatData[i][a] = secondaryFloatData[i][b];
664: secondaryFloatData[i][b] = tmpF;
665: }
666: }
667: }).sort(sortOrder);
668:
669: return len;
670: }
671:
672: protected boolean truncateZeroItems() {
673: return true;
674: }
675:
676: // ---
677: private void initFilterPanel() {
678: filterComponent = new FilterComponent();
679: filterComponent.setEmptyFilterText(DEFAULT_FILTER_HINT);
680:
681: filterComponent
682: .addFilterItem(
683: new ImageIcon(
684: filterComponent
685: .getClass()
686: .getResource(
687: "/org/netbeans/lib/profiler/ui/resources/filterStartsWith.png")),
688: STARTS_WITH_FILTER_NAME,
689: CommonConstants.FILTER_STARTS_WITH); // NOI18N
690: filterComponent
691: .addFilterItem(
692: new ImageIcon(
693: filterComponent
694: .getClass()
695: .getResource(
696: "/org/netbeans/lib/profiler/ui/resources/filterContains.png")),
697: CONTAINS_FILTER_NAME,
698: CommonConstants.FILTER_CONTAINS); // NOI18N
699: filterComponent
700: .addFilterItem(
701: new ImageIcon(
702: filterComponent
703: .getClass()
704: .getResource(
705: "/org/netbeans/lib/profiler/ui/resources/filterEndsWith.png")),
706: ENDS_WITH_FILTER_NAME,
707: CommonConstants.FILTER_ENDS_WITH); // NOI18N
708: filterComponent
709: .addFilterItem(
710: new ImageIcon(
711: filterComponent
712: .getClass()
713: .getResource(
714: "/org/netbeans/lib/profiler/ui/resources/filterRegExp.png")),
715: REGEXP_FILTER_NAME,
716: CommonConstants.FILTER_REGEXP); // NOI18N
717:
718: filterComponent.setFilterValues(filterString, filterType);
719: filterComponent
720: .addFilterListener(new FilterComponent.FilterListener() {
721: public void filterChanged() {
722: String selectedRowContents = null;
723: int selectedRow = resTable.getSelectedRow();
724:
725: if (selectedRow != -1) {
726: selectedRowContents = (String) resTable
727: .getValueAt(selectedRow, 0);
728: }
729:
730: filterString = filterComponent
731: .getFilterString();
732: filterType = filterComponent.getFilterType();
733: createFilteredIndexes();
734: resTable.invalidate();
735: jScrollPane.revalidate();
736: resTable.repaint();
737:
738: if (selectedRowContents != null) {
739: resTable.selectRowByContents(
740: selectedRowContents, 0, true);
741: }
742: }
743: });
744:
745: add(filterComponent, BorderLayout.SOUTH);
746: }
747:
748: private boolean passesFilter(int idx, String filter) {
749: String value = sortedClassNames[idx];
750:
751: if ("".equals(filter)) {
752: return true; // NOI18N
753: }
754:
755: // Case sensitive comparison:
756: /*switch (type) {
757: case CommonConstants.FILTER_STARTS_WITH:
758: return value.startsWith(filter);
759: case CommonConstants.FILTER_CONTAINS:
760: return value.indexOf(filter) != -1;
761: case CommonConstants.FILTER_ENDS_WITH:
762: return value.endsWith(filter);
763: case CommonConstants.FILTER_EQUALS:
764: return value.equals(filter);
765: case CommonConstants.FILTER_REGEXP:
766: return value.matches(filter);
767: }*/
768:
769: // Case insensitive comparison (except regexp):
770: switch (filterType) {
771: case CommonConstants.FILTER_STARTS_WITH:
772: return value.regionMatches(true, 0, filter, 0, filter
773: .length()); // case insensitive startsWith, optimized
774: case CommonConstants.FILTER_CONTAINS:
775: return value.toLowerCase().indexOf(filter.toLowerCase()) != -1; // case insensitive indexOf, NOT OPTIMIZED!!!
776: case CommonConstants.FILTER_ENDS_WITH:
777: return value.regionMatches(true, value.length()
778: - filter.length(), filter, 0, filter.length()); // case insensitive endsWith, optimized
779: case CommonConstants.FILTER_EQUALS:
780: return value.equalsIgnoreCase(filter); // case insensitive equals
781: case CommonConstants.FILTER_REGEXP:
782: return value.matches(filter); // still case sensitive!
783: }
784:
785: return false;
786: }
787:
788: private boolean passesFilters(int idx) {
789: if (filterType == CommonConstants.FILTER_NONE) {
790: return true;
791: }
792:
793: String[] filters = FilterComponent
794: .getFilterStrings(filterString);
795:
796: if (filters == null) {
797: return true;
798: }
799:
800: for (int i = 0; i < filters.length; i++) {
801: if (passesFilter(idx, filters[i])) {
802: return true;
803: }
804: }
805:
806: return false;
807: }
808: }
|