001: /*
002: * soapUI, copyright (C) 2004-2007 eviware.com
003: *
004: * soapUI is free software; you can redistribute it and/or modify it under the
005: * terms of version 2.1 of the GNU Lesser General Public License as published by
006: * the Free Software Foundation.
007: *
008: * soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009: * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
010: * See the GNU Lesser General Public License for more details at gnu.org.
011: */
012:
013: package com.eviware.soapui.impl.wsdl.panels.loadtest;
014:
015: import java.awt.Color;
016: import java.awt.Dimension;
017: import java.awt.Graphics;
018: import java.awt.Rectangle;
019: import java.awt.event.ComponentAdapter;
020: import java.awt.event.ComponentEvent;
021: import java.awt.event.MouseEvent;
022: import java.awt.event.MouseMotionListener;
023: import java.awt.image.BufferedImage;
024:
025: import javax.swing.BorderFactory;
026: import javax.swing.Icon;
027: import javax.swing.ImageIcon;
028: import javax.swing.JComponent;
029: import javax.swing.JLabel;
030: import javax.swing.Scrollable;
031: import javax.swing.event.TableModelEvent;
032: import javax.swing.event.TableModelListener;
033: import javax.swing.table.TableModel;
034:
035: import com.eviware.soapui.impl.wsdl.loadtest.ColorPalette;
036: import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
037: import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
038: import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics.Statistic;
039: import com.eviware.soapui.impl.wsdl.loadtest.data.StatisticsHistory.StatisticsHistoryModel;
040: import com.jgoodies.forms.builder.ButtonBarBuilder;
041:
042: /**
043: * Graphical representation of testschedule statistics
044: *
045: * @author Ole.Matzura
046: */
047:
048: public class JStatisticsHistoryGraph extends JComponent implements
049: Scrollable {
050: private static final Color THREADCOUNT_COLOR = Color.GREEN.darker();
051: private static final int SCROLL_AHEAD = 50;
052: private static final Color TOTAL_COLOR = Color.BLACK;
053:
054: private final WsdlLoadTest loadTest;
055: private final LoadTestStatistics statisticsModel;
056: private StatisticsHistoryModel data;
057: private JComponent legend;
058: private InternalTableModelListener tableModelListener = new InternalTableModelListener();
059: private long[] maxValues;
060: private float[] scales;
061:
062: public JStatisticsHistoryGraph(WsdlLoadTest loadTest) {
063: this .loadTest = loadTest;
064: this .statisticsModel = loadTest.getStatisticsModel();
065: this .data = statisticsModel.getHistory()
066: .getStatisticsValueHistory(Statistic.AVERAGE);
067:
068: setAutoscrolls(true);
069: addMouseMotionListener(new InternalMouseMotionListener());
070:
071: data.addTableModelListener(tableModelListener);
072:
073: initMaxValues();
074: initScales();
075:
076: setBackground(Color.WHITE);
077: setOpaque(true);
078:
079: addComponentListener(new ComponentAdapter() {
080:
081: public void componentResized(ComponentEvent e) {
082: initScales();
083: }
084: });
085: }
086:
087: public long getResolution() {
088: return statisticsModel.getHistory().getResolution();
089: }
090:
091: public void setResolution(long resolution) {
092: statisticsModel.getHistory().setResolution(resolution);
093: }
094:
095: public TableModel getModel() {
096: return data;
097: }
098:
099: public void release() {
100: data.removeTableModelListener(tableModelListener);
101: }
102:
103: public void setStatistic(Statistic statistic) {
104: if (data != null) {
105: data.removeTableModelListener(tableModelListener);
106: data.release();
107: }
108:
109: data = statisticsModel.getHistory().getStatisticsValueHistory(
110: statistic);
111:
112: initMaxValues();
113: initScales();
114:
115: data.addTableModelListener(tableModelListener);
116:
117: getParent().invalidate();
118: revalidate();
119: repaint();
120: }
121:
122: private void initMaxValues() {
123: maxValues = new long[data.getColumnCount()];
124:
125: for (int c = 0; c < data.getRowCount(); c++) {
126: for (int i = 0; i < data.getColumnCount(); i++) {
127: long value = (Long) data.getValueAt(c, i);
128: if (value > maxValues[i])
129: maxValues[i] = value;
130: }
131: }
132: }
133:
134: private void initScales() {
135: scales = new float[maxValues.length];
136:
137: for (int c = 0; c < maxValues.length; c++) {
138: recalcScale(c);
139: }
140: }
141:
142: private boolean recalcScale(int index) {
143: float scale = (index == 0 || maxValues[index] == 0) ? 1
144: : (float) (getHeight())
145: / (float) (maxValues[index] + 10);
146: if (scale > 1)
147: scale = 1;
148:
149: if (Float.compare(scale, scales[index]) == 0) {
150: return false;
151: }
152:
153: scales[index] = scale;
154: return true;
155: }
156:
157: public void paintComponent(Graphics g) {
158: g.setColor(getBackground());
159:
160: Rectangle clip = g.getClipBounds();
161: g.fillRect((int) clip.getX(), (int) clip.getY(), (int) clip
162: .getWidth(), (int) clip.getHeight());
163:
164: double right = clip.getX() + clip.getWidth();
165: int height = getHeight();
166:
167: for (int c = (int) clip.getX(); c < data.getRowCount()
168: && c < right; c++) {
169: for (int i = 0; i < data.getColumnCount(); i++) {
170: if (i == 0)
171: g.setColor(THREADCOUNT_COLOR);
172: else if (i == data.getColumnCount() - 1)
173: g.setColor(TOTAL_COLOR);
174: else
175: g.setColor(ColorPalette.getColor(loadTest
176: .getTestCase().getTestStepAt(i - 1)));
177:
178: int yOffset = (int) ((float) ((Long) data.getValueAt(c,
179: i)) * scales[i]);
180:
181: if (clip.contains(c, height - yOffset - 1)) {
182: g.drawLine(c, height - yOffset - 1, c, height
183: - yOffset - 1);
184: }
185: }
186: }
187: }
188:
189: public JComponent getLegend() {
190: if (legend == null)
191: buildLegend();
192:
193: return legend;
194: }
195:
196: private void buildLegend() {
197: ButtonBarBuilder builder = new ButtonBarBuilder();
198:
199: builder.addFixed(new JLabel("ThreadCount",
200: createLegendIcon(THREADCOUNT_COLOR), JLabel.LEFT));
201: builder.addUnrelatedGap();
202: builder.addFixed(new JLabel("Total",
203: createLegendIcon(TOTAL_COLOR), JLabel.LEFT));
204: builder.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
205:
206: legend = builder.getPanel();
207: }
208:
209: private Icon createLegendIcon(Color color) {
210: BufferedImage image = new BufferedImage(10, 10,
211: BufferedImage.TYPE_3BYTE_BGR);
212: Graphics g = image.getGraphics();
213: g.setColor(color);
214: g.fillRect(1, 1, 8, 8);
215: g.setColor(Color.DARK_GRAY);
216: g.drawRect(0, 0, 10, 10);
217: return new ImageIcon(image);
218: }
219:
220: public Dimension getPreferredScrollableViewportSize() {
221: return getPreferredSize();
222: }
223:
224: public Dimension getPreferredSize() {
225: int height = getHeight();
226: int width = data.getRowCount() + SCROLL_AHEAD;
227: return new Dimension(width, height);
228: }
229:
230: public int getScrollableUnitIncrement(Rectangle visibleRect,
231: int orientation, int direction) {
232: return 1;
233: }
234:
235: public int getScrollableBlockIncrement(Rectangle visibleRect,
236: int orientation, int direction) {
237: return 10;
238: }
239:
240: public boolean getScrollableTracksViewportWidth() {
241: return false;
242: }
243:
244: public boolean getScrollableTracksViewportHeight() {
245: return true;
246: }
247:
248: private final class InternalTableModelListener implements
249: TableModelListener {
250: public synchronized void tableChanged(TableModelEvent e) {
251: boolean repaint = false;
252:
253: if (e.getType() == TableModelEvent.INSERT) {
254: int firstRow = e.getFirstRow();
255: int lastRow = e.getLastRow();
256: int height = getHeight();
257:
258: for (int c = firstRow; c <= lastRow; c++) {
259: for (int i = 0; i < data.getColumnCount(); i++) {
260: long value = (Long) data.getValueAt(c, i);
261:
262: if (value > maxValues[i]) {
263: maxValues[i] = value;
264: repaint = recalcScale(i);
265: }
266: }
267: }
268:
269: if (!repaint) {
270: Rectangle rect = new Rectangle(firstRow, 0,
271: (lastRow - firstRow) + 1, height);
272: repaint(rect);
273: }
274:
275: Dimension size = getSize();
276: Rectangle r = getVisibleRect();
277:
278: double x2 = r.getX() + r.getWidth();
279: if (x2 >= data.getRowCount()
280: && x2 < data.getRowCount() + SCROLL_AHEAD) {
281: scrollRectToVisible(new Rectangle(firstRow
282: + SCROLL_AHEAD / 2, 0,
283: (lastRow - firstRow) + 1, height));
284: }
285:
286: if (!repaint
287: && size.getWidth() < data.getRowCount()
288: + SCROLL_AHEAD) {
289: revalidate();
290: }
291: } else if (e.getType() == TableModelEvent.UPDATE) {
292: initMaxValues();
293: initScales();
294:
295: repaint = true;
296: }
297:
298: if (repaint) {
299: getParent().invalidate();
300: revalidate();
301: repaint();
302: }
303: }
304: }
305:
306: private class InternalMouseMotionListener implements
307: MouseMotionListener {
308: public void mouseDragged(MouseEvent e) {
309: Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
310: scrollRectToVisible(r);
311: }
312:
313: public void mouseMoved(MouseEvent e) {
314: }
315: }
316: }
|