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.graphs;
042:
043: import org.netbeans.lib.profiler.ui.UIUtils;
044: import org.netbeans.lib.profiler.ui.charts.ChartModelListener;
045: import org.netbeans.lib.profiler.ui.charts.SynchronousXYChart;
046: import org.netbeans.lib.profiler.ui.components.ColorIcon;
047: import org.netbeans.lib.profiler.ui.monitor.VMTelemetryXYChartModel;
048: import org.netbeans.lib.profiler.ui.monitor.VMTelemetryXYChartModelDataResetListener;
049: import java.awt.*;
050: import java.awt.event.InputEvent;
051: import java.awt.event.MouseAdapter;
052: import java.awt.event.MouseEvent;
053: import java.util.ResourceBundle;
054: import javax.swing.*;
055: import javax.swing.border.LineBorder;
056:
057: /**
058: * A panel with graph representing target application threads.
059: *
060: * @author Tomas Hurka
061: * @author Vladislav Nemec
062: * @author Ian Formanek
063: * @author Jiri Sedlacek
064: */
065: public class ThreadsGraphPanel extends GraphPanel implements
066: ChartModelListener, VMTelemetryXYChartModelDataResetListener {
067: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
068:
069: // -----
070: // I18N String constants
071: private static final ResourceBundle messages = ResourceBundle
072: .getBundle("org.netbeans.lib.profiler.ui.graphs.Bundle"); // NOI18N
073: private static final String THREADS_CURRENT_STRING = messages
074: .getString("ThreadsGraphPanel_ThreadsCurrentString"); // NOI18N
075: private static final String THREADS_MAXIMUM_STRING = messages
076: .getString("ThreadsGraphPanel_ThreadsMaximumString"); // NOI18N
077: private static final String TIME_AT_CURSOR_STRING = messages
078: .getString("ThreadsGraphPanel_TimeAtCursorString"); // NOI18N
079: private static final String THREADS_AT_CURSOR_STRING = messages
080: .getString("ThreadsGraphPanel_ThreadsAtCursorString"); // NOI18N
081: private static final String CLASSES_CURRENT_STRING = messages
082: .getString("ThreadsGraphPanel_ClassesCurrentString"); // NOI18N
083: private static final String CLASSES_MAXIMUM_STRING = messages
084: .getString("ThreadsGraphPanel_ClassesMaximumString"); // NOI18N
085: private static final String CLASSES_AT_CURSOR_STRING = messages
086: .getString("ThreadsGraphPanel_ClassesAtCursorString"); // NOI18N
087: private static final String CHART_ACCESS_NAME = messages
088: .getString("ThreadsGraphPanel_ChartAccessName"); // NOI18N
089: // -----
090:
091: //~ Instance fields ----------------------------------------------------------------------------------------------------------
092:
093: private JPanel bigLegendPanel;
094: private JPanel smallLegendPanel;
095: private SynchronousXYChart xyChart;
096: private VMTelemetryXYChartModel threadsXYChartModel;
097: private volatile boolean completeFunctionality;
098: private int chartTimeLength = 180000; // 3 minutes to switch from fitToWindow to trackingEnd
099:
100: //private int chartTimeLength = 10000; // 10 seconds for testing purposes
101:
102: //~ Constructors -------------------------------------------------------------------------------------------------------------
103:
104: /**
105: * Creates new form ThreadsGraphPanel with the default history size (3 minutes)
106: * and no mouse zooming capabilities
107: * @param telemetryManager The data manager for telemetry data
108: */
109: public ThreadsGraphPanel(
110: final VMTelemetryXYChartModel threadsXYChartModel,
111: final Action detailsAction) {
112: this (false, null, threadsXYChartModel, null);
113: }
114:
115: /**
116: * Creates new form ThreadsGraphPanel with the given amount of history to keep
117: *
118: * @param completeFunctionality if true, the chart can be zoomed using mouse and will display all history, if false, it will only display last session and 3 minutes of data
119: * @param backgroundPaint paint used for drawing graph background
120: * @param telemetryManager The data manager for telemetry data
121: */
122: public ThreadsGraphPanel(final boolean completeFunctionality,
123: final Color backgroundPaint,
124: final VMTelemetryXYChartModel threadsXYChartModel,
125: final Action detailsAction) {
126: this .completeFunctionality = completeFunctionality;
127: this .threadsXYChartModel = threadsXYChartModel;
128:
129: threadsXYChartModel.addDataResetListener(this );
130:
131: setLayout(new java.awt.BorderLayout());
132:
133: // --- Big legend panel ----------------------------------------------------
134: JLabel userThreadsBig = new JLabel(threadsXYChartModel
135: .getSeriesName(0), new ColorIcon(threadsXYChartModel
136: .getSeriesColor(0), Color.BLACK, 18, 9),
137: SwingConstants.LEADING);
138: userThreadsBig.setBorder(BorderFactory.createEmptyBorder(0, 5,
139: 0, 0));
140:
141: JLabel loadedClassesBig = new JLabel(threadsXYChartModel
142: .getSeriesName(1), new ColorIcon(threadsXYChartModel
143: .getSeriesColor(1), Color.BLACK, 18, 9),
144: SwingConstants.LEADING);
145: loadedClassesBig.setBorder(BorderFactory.createEmptyBorder(0,
146: 5, 0, 0));
147:
148: bigLegendPanel = new JPanel();
149: bigLegendPanel.add(userThreadsBig);
150: bigLegendPanel.add(loadedClassesBig);
151:
152: // --- Small legend panel --------------------------------------------------
153: JLabel userThreadsSmall = new JLabel(threadsXYChartModel
154: .getSeriesName(0), new ColorIcon(threadsXYChartModel
155: .getSeriesColor(0), null, 8, 8), SwingConstants.LEADING);
156: userThreadsSmall.setFont(getFont().deriveFont(
157: (float) (getFont().getSize()) - 1));
158: userThreadsSmall.setBorder(BorderFactory.createEmptyBorder(0,
159: 5, 0, 5));
160:
161: JLabel loadedClassesSmall = new JLabel(threadsXYChartModel
162: .getSeriesName(1), new ColorIcon(threadsXYChartModel
163: .getSeriesColor(1), null, 8, 8), SwingConstants.LEADING);
164: loadedClassesSmall.setFont(getFont().deriveFont(
165: (float) (getFont().getSize()) - 1));
166: loadedClassesSmall.setBorder(BorderFactory.createEmptyBorder(0,
167: 5, 0, 5));
168:
169: smallLegendPanel = new JPanel();
170: smallLegendPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0,
171: 1));
172: smallLegendPanel.setBackground(Color.WHITE);
173: smallLegendPanel.setBorder(new LineBorder(new Color(235, 235,
174: 235), 1));
175: smallLegendPanel.add(userThreadsSmall);
176: smallLegendPanel.add(loadedClassesSmall);
177:
178: // --- Chart panel ---------------------------------------------------------
179: xyChart = new SynchronousXYChart(SynchronousXYChart.TYPE_LINE,
180: SynchronousXYChart.VALUES_DISCRETE, 0.01) {
181: public String getToolTipText(MouseEvent event) {
182: return getChartToolTipText(event);
183: }
184: };
185:
186: xyChart.setUseSecondaryVerticalAxis(true);
187:
188: if (completeFunctionality) {
189: xyChart.setTopChartMargin(50);
190: xyChart.allowSelection();
191: xyChart.setMinimumVerticalMarksDistance(50);
192: } else {
193: xyChart.setTopChartMargin(20);
194: xyChart.denySelection();
195: xyChart.setMinimumVerticalMarksDistance(UIManager.getFont(
196: "Panel.font").getSize() + 8); // NOI18N
197: }
198:
199: chartDataReset();
200:
201: if (backgroundPaint != null) {
202: setOpaque(true);
203: setBackground(backgroundPaint);
204: xyChart.setBackgroundPaint(backgroundPaint);
205: }
206:
207: xyChart.setModel(threadsXYChartModel);
208:
209: if (!completeFunctionality) {
210: threadsXYChartModel.addChartModelListener(this ); // Needs to be AFTER xyChart.setModel() !!!
211: }
212:
213: long time = System.currentTimeMillis();
214: xyChart.setupInitialAppearance(time, time + 1200, 0, 2);
215: getAccessibleContext().setAccessibleName(CHART_ACCESS_NAME);
216: xyChart.setAccessibleContext(getAccessibleContext());
217:
218: add(xyChart);
219:
220: xyChart.addMouseListener(new MouseAdapter() {
221: public void mouseClicked(MouseEvent e) {
222: if ((e.getModifiers() == InputEvent.BUTTON1_MASK)
223: && (e.getClickCount() == 2)) {
224: if (detailsAction != null) {
225: detailsAction.actionPerformed(null);
226: }
227: }
228: }
229: });
230:
231: ToolTipManager.sharedInstance().registerComponent(xyChart);
232: }
233:
234: //~ Methods ------------------------------------------------------------------------------------------------------------------
235:
236: public JPanel getBigLegendPanel() {
237: return bigLegendPanel;
238: }
239:
240: // ----------------------------------------------------------------------------
241: // Public API
242: public SynchronousXYChart getChart() {
243: return xyChart;
244: }
245:
246: public String getChartToolTipText(MouseEvent event) {
247: if (threadsXYChartModel.getItemCount() < 2) {
248: return null;
249: }
250:
251: StringBuffer toolTipBuffer = new StringBuffer();
252:
253: toolTipBuffer.append("<html>"); // NOI18N
254:
255: if (!completeFunctionality
256: || !xyChart.hasValidDataForPosition(event.getX(), event
257: .getY())) {
258: appendToolTipItem(
259: toolTipBuffer,
260: THREADS_CURRENT_STRING,
261: intFormat.format(threadsXYChartModel.getYValue(
262: threadsXYChartModel.getItemCount() - 1, 0)),
263: false);
264: appendToolTipItem(toolTipBuffer, THREADS_MAXIMUM_STRING,
265: intFormat.format(threadsXYChartModel
266: .getMaxYValue(0)), false);
267: appendToolTipItem(
268: toolTipBuffer,
269: CLASSES_CURRENT_STRING,
270: intFormat.format(threadsXYChartModel.getYValue(
271: threadsXYChartModel.getItemCount() - 1, 1)),
272: false);
273: appendToolTipItem(toolTipBuffer, CLASSES_MAXIMUM_STRING,
274: intFormat.format(threadsXYChartModel
275: .getMaxYValue(1)), true);
276: } else {
277: appendToolTipItem(
278: toolTipBuffer,
279: THREADS_CURRENT_STRING,
280: intFormat.format(threadsXYChartModel.getYValue(
281: threadsXYChartModel.getItemCount() - 1, 0)),
282: false);
283: appendToolTipItem(toolTipBuffer, THREADS_MAXIMUM_STRING,
284: intFormat.format(threadsXYChartModel
285: .getMaxYValue(0)), false);
286: appendToolTipItem(
287: toolTipBuffer,
288: CLASSES_CURRENT_STRING,
289: intFormat.format(threadsXYChartModel.getYValue(
290: threadsXYChartModel.getItemCount() - 1, 1)),
291: false);
292: appendToolTipItem(toolTipBuffer, CLASSES_MAXIMUM_STRING,
293: intFormat.format(threadsXYChartModel
294: .getMaxYValue(1)), false);
295:
296: toolTipBuffer.append("<br>"); // NOI18N
297:
298: appendToolTipItem(toolTipBuffer, TIME_AT_CURSOR_STRING,
299: xyChart.getTimeAtPosition(event.getX()), false);
300: appendToolTipItem(toolTipBuffer, THREADS_AT_CURSOR_STRING,
301: intFormat.format(xyChart.getYValueAtPosition(event
302: .getX(), 0)), false);
303: appendToolTipItem(toolTipBuffer, CLASSES_AT_CURSOR_STRING,
304: intFormat.format(xyChart.getYValueAtPosition(event
305: .getX(), 1)), true);
306: }
307:
308: toolTipBuffer.append("</html>"); // NOI18N
309:
310: return toolTipBuffer.toString();
311: }
312:
313: public JPanel getSmallLegendPanel() {
314: return smallLegendPanel;
315: }
316:
317: // --- ChartModelListener ----------------------------------------------------
318: public void chartDataChanged() {
319: if (!completeFunctionality) {
320: if (xyChart.isFitToWindow()
321: && ((threadsXYChartModel.getMaxXValue() - threadsXYChartModel
322: .getMinXValue()) >= chartTimeLength)) { // after 3 minutes switch from fitToWindow to trackingEnd
323: UIUtils.runInEventDispatchThread(new Runnable() {
324: public void run() {
325: xyChart.setTrackingEnd();
326: }
327: });
328: }
329: }
330: }
331:
332: // --- VMTelemetryXYChartModelDataResetListener ------------------------------
333: public void chartDataReset() {
334: UIUtils.runInEventDispatchThread(new Runnable() {
335: public void run() {
336: xyChart.resetChart();
337:
338: if (completeFunctionality) {
339: //xyChart.setScale(0.01);
340: //xyChart.setViewOffsetX(0);
341: xyChart.resetTrackingEnd();
342: xyChart.resetFitToWindow();
343: } else {
344: xyChart.setFitToWindow();
345: }
346: }
347: });
348: }
349:
350: // --- ToolTip stuff ---------------------------------------------------------
351: private static void appendToolTipItem(StringBuffer toolTipBuffer,
352: String itemName, String itemValue, boolean lastItem) {
353: toolTipBuffer.append(" <b>"); // NOI18N
354: toolTipBuffer.append(itemName);
355: toolTipBuffer.append("</b>: "); // NOI18N
356: toolTipBuffer.append(itemValue);
357: toolTipBuffer.append(" "); // NOI18N
358:
359: if (!lastItem) {
360: toolTipBuffer.append("<br>"); // NOI18N
361: }
362: }
363: }
|