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: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.timers;
042:
043: import java.awt.BorderLayout;
044: import java.awt.Component;
045: import java.awt.Dialog;
046: import java.awt.Point;
047: import java.awt.event.ActionEvent;
048: import java.awt.event.MouseEvent;
049: import java.beans.PropertyChangeEvent;
050: import java.beans.PropertyChangeListener;
051: import java.io.IOException;
052: import java.lang.ref.Reference;
053: import java.lang.ref.WeakReference;
054: import java.util.Collection;
055: import java.util.Collections;
056: import java.util.HashMap;
057: import java.util.Iterator;
058: import java.util.Map;
059: import javax.swing.*;
060: import javax.swing.event.ChangeEvent;
061: import javax.swing.event.ChangeListener;
062: import javax.swing.table.DefaultTableModel;
063: import org.netbeans.insane.live.LiveReferences;
064: import org.openide.DialogDescriptor;
065: import org.openide.explorer.view.NodeRenderer;
066: import org.openide.filesystems.FileObject;
067: import org.openide.loaders.DataObject;
068: import org.openide.loaders.DataObjectNotFoundException;
069: import org.openide.nodes.Node;
070: import org.openide.DialogDisplayer;
071:
072: /**
073: *
074: * @author Jan Lahoda
075: */
076: public class TimeComponentPanel extends javax.swing.JPanel implements
077: PropertyChangeListener {
078:
079: /** Creates new form TimeComponentPanel */
080: public TimeComponentPanel() {
081: initComponents();
082: times.addMouseListener(new PopupAdapter());
083: jList1.addMouseListener(new ListPopupAdapter());
084: key2RowNumber = new HashMap<String, Integer>();
085: TimesCollectorPeer.getDefault().addPropertyChangeListener(this );
086: fillIn();
087: }
088:
089: /** This method is called from within the constructor to
090: * initialize the form.
091: * WARNING: Do NOT modify this code. The content of this method is
092: * always regenerated by the Form Editor.
093: */
094: // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
095: private void initComponents() {
096: java.awt.GridBagConstraints gridBagConstraints;
097:
098: jSplitPane1 = new javax.swing.JSplitPane();
099: jScrollPane1 = new javax.swing.JScrollPane();
100: jList1 = new javax.swing.JList();
101: jScrollPane2 = new javax.swing.JScrollPane();
102: times = new javax.swing.JTable();
103:
104: setLayout(new java.awt.GridBagLayout());
105:
106: jSplitPane1.setDividerLocation(100);
107: jSplitPane1.setDividerSize(8);
108: jSplitPane1
109: .setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
110: jList1.setModel(new DefaultListModel());
111: jList1.setCellRenderer(new FileObjectListRenderer());
112: jList1
113: .addListSelectionListener(new javax.swing.event.ListSelectionListener() {
114: public void valueChanged(
115: javax.swing.event.ListSelectionEvent evt) {
116: jList1ValueChanged(evt);
117: }
118: });
119:
120: jScrollPane1.setViewportView(jList1);
121:
122: jSplitPane1.setLeftComponent(jScrollPane1);
123:
124: times.setModel(new javax.swing.table.DefaultTableModel(
125: new Object[][] {
126:
127: }, new String[] { "Name", "Time" }) {
128: Class[] types = new Class[] { java.lang.Object.class,
129: java.lang.Long.class };
130: boolean[] canEdit = new boolean[] { false, false };
131:
132: public Class getColumnClass(int columnIndex) {
133: return types[columnIndex];
134: }
135:
136: public boolean isCellEditable(int rowIndex, int columnIndex) {
137: return canEdit[columnIndex];
138: }
139: });
140: jScrollPane2.setViewportView(times);
141:
142: jSplitPane1.setRightComponent(jScrollPane2);
143:
144: gridBagConstraints = new java.awt.GridBagConstraints();
145: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
146: gridBagConstraints.weightx = 1.0;
147: gridBagConstraints.weighty = 1.0;
148: add(jSplitPane1, gridBagConstraints);
149:
150: }
151:
152: // </editor-fold>//GEN-END:initComponents
153:
154: private void jList1ValueChanged(
155: javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_jList1ValueChanged
156: // TODO add your handling code here:
157: fillTimeTable();
158: }//GEN-LAST:event_jList1ValueChanged
159:
160: // Variables declaration - do not modify//GEN-BEGIN:variables
161: private javax.swing.JList jList1;
162: private javax.swing.JScrollPane jScrollPane1;
163: private javax.swing.JScrollPane jScrollPane2;
164: private javax.swing.JSplitPane jSplitPane1;
165: private javax.swing.JTable times;
166: // End of variables declaration//GEN-END:variables
167:
168: private Map<String, Integer> key2RowNumber;
169:
170: private void fillTimeTable() {
171: Reference ref = (Reference) jList1.getSelectedValue();
172: Object fo = ref == null ? null : ref.get();
173:
174: // clear the table
175: DefaultTableModel model = (DefaultTableModel) times.getModel();
176: while (model.getRowCount() > 0) {
177: model.removeRow(0);
178: }
179: key2RowNumber.clear();
180:
181: if (fo == null)
182: return;
183:
184: Collection<String> keys = TimesCollectorPeer.getDefault()
185: .getKeysForFile(fo);
186: synchronized (keys) {
187: for (String key : keys) {
188: changeRow(fo, key);
189: }
190: }
191: }
192:
193: private TimesCollectorPeer.Description getDescForRow(Object fo,
194: int row) {
195: Collection<String> keys = TimesCollectorPeer.getDefault()
196: .getKeysForFile(fo);
197: synchronized (keys) {
198: Iterator<String> it = keys.iterator();
199: String key = null;
200: for (int i = 0; i <= row; i++) {
201: assert (it.hasNext());
202: key = it.next();
203: }
204: return TimesCollectorPeer.getDefault().getDescription(fo,
205: key);
206: }
207: }
208:
209: private void changeRow(Object fo, String key) {
210: Integer row = key2RowNumber.get(key);
211: DefaultTableModel model = (DefaultTableModel) times.getModel();
212:
213: if (row != null) {
214: model.removeRow(row);
215: }
216:
217: TimesCollectorPeer.Description desc = TimesCollectorPeer
218: .getDefault().getDescription(fo, key);
219:
220: if (desc == null) {
221: return;
222: }
223:
224: if (row == null) {
225: key2RowNumber.put(key, row = model.getRowCount());
226: }
227:
228: model.insertRow(row, new Object[] { desc.getMessage(),
229: desc.getTime() });
230: }
231:
232: private void fillIn() {
233: DefaultListModel model = (DefaultListModel) jList1.getModel();
234:
235: model.removeAllElements();
236:
237: for (Object f : TimesCollectorPeer.getDefault().getFiles()) {
238: model.addElement(new WeakReference<Object>(f));
239: }
240: }
241:
242: public void propertyChange(final PropertyChangeEvent evt) {
243: SwingUtilities.invokeLater(new Runnable() {
244: public void run() {
245: if ("fos".equals(evt.getPropertyName())) {
246: DefaultListModel model = (DefaultListModel) jList1
247: .getModel();
248:
249: if (evt.getNewValue() != null) {
250: model.addElement(new WeakReference<Object>(evt
251: .getNewValue()));
252: } else {
253: fillIn();
254: }
255: }
256:
257: if ("PROP".equals(evt.getPropertyName())) {
258: Object fo = evt.getOldValue();
259: String key = (String) evt.getNewValue();
260: Reference ref = (Reference) jList1
261: .getSelectedValue();
262: if (fo == null || (ref != null && ref.get() == fo)) {
263: changeRow(fo, key);
264: }
265: }
266:
267: if ("selected".equals(evt.getPropertyName())) {
268: Object fo = evt.getNewValue();
269: ListModel dm = jList1.getModel();
270: for (int i = 0; i < dm.getSize(); i++) {
271: if (fo.equals(((WeakReference) dm
272: .getElementAt(i)).get())) {
273: jList1.setSelectedIndex(i);
274: jList1.ensureIndexIsVisible(i);
275: repaint();
276: break;
277: }
278: }
279: }
280: }
281: });
282: }
283:
284: private static void dumpRoots(Collection objs) {
285: JPanel inner = new JPanel();
286: inner.setLayout(new BorderLayout());
287: JProgressBar bar = new JProgressBar();
288: inner.add(new JLabel("Computing object reachability"),
289: BorderLayout.CENTER);
290: inner.add(bar, BorderLayout.SOUTH);
291: Dialog d = DialogDisplayer.getDefault().createDialog(
292: new DialogDescriptor(inner, "Please wait"));
293: d.pack();
294: d.setModal(false);
295: d.setVisible(true);
296:
297: String report = getRoots(objs, bar, inner);
298:
299: inner.removeAll();
300: JScrollPane pane = new JScrollPane();
301: JTextArea editor = new JTextArea(report);
302: editor.setColumns(80);
303: editor.setEditable(false);
304: pane.setViewportView(editor);
305: inner.add(pane, BorderLayout.CENTER);
306: d.setSize(Math.min(600, editor.getPreferredSize().width + 30),
307: Math.min(400, editor.getPreferredSize().height + 70));
308: d.invalidate();
309: d.validate();
310: d.repaint();
311: }
312:
313: private static class FileObjectListRenderer extends
314: DefaultListCellRenderer {
315: private NodeRenderer r;
316:
317: private FileObjectListRenderer() {
318: r = new NodeRenderer();
319: }
320:
321: public Component getListCellRendererComponent(JList list,
322: Object wr, int index, boolean isSelected,
323: boolean cellHasFocus) {
324: Object value = ((WeakReference) wr).get();
325:
326: if (value instanceof FileObject) {
327: try {
328: FileObject fo = (FileObject) value;
329: DataObject od = DataObject.find(fo);
330: Node node = od.getNodeDelegate();
331:
332: return r.getListCellRendererComponent(list, node,
333: index, isSelected, cellHasFocus);
334: } catch (IOException e) {
335: FileObject fo = (FileObject) value;
336: value = "FO: " + fo;
337: }
338: }
339:
340: return super .getListCellRendererComponent(list, value,
341: index, isSelected, cellHasFocus);
342: }
343:
344: }
345:
346: class PopupAdapter extends
347: org.openide.awt.MouseUtils.PopupMouseAdapter {
348: PopupAdapter() {
349: }
350:
351: protected void showPopup(MouseEvent e) {
352: int selRow = times.rowAtPoint(e.getPoint());
353:
354: if (!times.isRowSelected(selRow)) {
355: // This will set ExplorerManager selection as well.
356: // If selRow == -1 the selection will be cleared.
357: times.getSelectionModel().setSelectionInterval(selRow,
358: selRow);
359: }
360:
361: if (selRow != -1) {
362: Point p = SwingUtilities.convertPoint(e.getComponent(),
363: e.getX(), e.getY(), TimeComponentPanel.this );
364: createPopup((int) p.getX(), (int) p.getY(), selRow);
365: }
366: }
367:
368: void createPopup(int x, int y, int row) {
369: Object fo = jList1.getSelectedValue();
370: if (fo instanceof WeakReference)
371: fo = ((WeakReference) fo).get();
372: if (fo == null)
373: return;
374:
375: TimesCollectorPeer.Description desc = getDescForRow(fo, row);
376: if (!(desc instanceof TimesCollectorPeer.ObjectCountDescripton))
377: return;
378:
379: final TimesCollectorPeer.ObjectCountDescripton oc = (TimesCollectorPeer.ObjectCountDescripton) desc;
380: JPopupMenu popup = new JPopupMenu();
381: popup.add(new AbstractAction("Find refs") {
382:
383: public void actionPerformed(ActionEvent arg0) {
384: dumpRoots(oc.getInstances());
385: }
386: });
387: popup.show(TimeComponentPanel.this , x, y);
388: }
389: }
390:
391: class ListPopupAdapter extends
392: org.openide.awt.MouseUtils.PopupMouseAdapter {
393: ListPopupAdapter() {
394: }
395:
396: protected void showPopup(MouseEvent e) {
397: int selRow = jList1.locationToIndex(e.getPoint());
398:
399: if (!jList1.isSelectedIndex(selRow)) {
400: // If selRow == -1 the selection will be cleared.
401: jList1.getSelectionModel().setSelectionInterval(selRow,
402: selRow);
403: }
404:
405: if (selRow != -1) {
406: Point p = SwingUtilities.convertPoint(e.getComponent(),
407: e.getX(), e.getY(), TimeComponentPanel.this );
408: createPopup((int) p.getX(), (int) p.getY(), selRow);
409: }
410: }
411:
412: void createPopup(int x, int y, int row) {
413: final WeakReference wr = (WeakReference) jList1
414: .getSelectedValue();
415: if (!(wr.get() instanceof FileObject))
416: return;
417:
418: JPopupMenu popup = new JPopupMenu();
419: popup.add(new AbstractAction("Find refs") {
420:
421: public void actionPerformed(ActionEvent arg0) {
422: try {
423: FileObject f = (FileObject) wr.get();
424: if (f == null)
425: return;
426: DataObject dobj = DataObject.find(f);
427: f = null;
428: // hack - DO.find, we'not really interrested in
429: // the FileObject reachability
430: // This will go away for general key type
431: dumpRoots(Collections.singleton(dobj));
432: } catch (DataObjectNotFoundException ex) {
433: // OK, the DO is not ready, FO invalid or whatever.
434: // Trace the invalid FO instead
435: dumpRoots(Collections.singleton(wr.get()));
436: }
437: }
438: });
439: popup.show(TimeComponentPanel.this , x, y);
440: }
441: }
442:
443: private static String getRoots(Collection objects,
444: JProgressBar bar, final JPanel inner) {
445: // scanning intentionally blocks AWT, force repaints
446: bar.getModel().addChangeListener(new ChangeListener() {
447: public void stateChanged(ChangeEvent arg0) {
448: inner.paintImmediately(inner.getBounds());
449: }
450: });
451: Map/*<Object,Path>*/traces = LiveReferences.fromRoots(objects,
452: null, bar.getModel());
453: StringBuffer sb = new StringBuffer();
454:
455: for (Object inst : traces.keySet()) {
456: sb.append(inst);
457: sb.append(":\n");
458: sb.append(traces.get(inst));
459: sb.append("\n\n");
460: }
461:
462: return sb.toString();
463: }
464: }
|