1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.panels.loadtest;
14
15 import java.awt.Color;
16 import java.awt.Dimension;
17 import java.awt.Graphics;
18 import java.awt.Rectangle;
19 import java.awt.event.ComponentAdapter;
20 import java.awt.event.ComponentEvent;
21 import java.awt.event.MouseEvent;
22 import java.awt.event.MouseMotionListener;
23 import java.awt.image.BufferedImage;
24
25 import javax.swing.BorderFactory;
26 import javax.swing.Icon;
27 import javax.swing.ImageIcon;
28 import javax.swing.JComponent;
29 import javax.swing.JLabel;
30 import javax.swing.Scrollable;
31 import javax.swing.event.TableModelEvent;
32 import javax.swing.event.TableModelListener;
33 import javax.swing.table.TableModel;
34
35 import com.eviware.soapui.impl.wsdl.loadtest.ColorPalette;
36 import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
37 import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
38 import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics.Statistic;
39 import com.eviware.soapui.impl.wsdl.loadtest.data.StatisticsHistory.StatisticsHistoryModel;
40 import com.jgoodies.forms.builder.ButtonBarBuilder;
41
42 /***
43 * Graphical representation of testschedule statistics
44 *
45 * @author Ole.Matzura
46 */
47
48 public class JStatisticsHistoryGraph extends JComponent implements Scrollable
49 {
50 private static final Color THREADCOUNT_COLOR = Color.GREEN.darker();
51 private static final int SCROLL_AHEAD = 50;
52 private static final Color TOTAL_COLOR = Color.BLACK;
53
54 private final WsdlLoadTest loadTest;
55 private final LoadTestStatistics statisticsModel;
56 private StatisticsHistoryModel data;
57 private JComponent legend;
58 private InternalTableModelListener tableModelListener = new InternalTableModelListener();
59 private long[] maxValues;
60 private float [] scales;
61
62 public JStatisticsHistoryGraph(WsdlLoadTest loadTest)
63 {
64 this.loadTest = loadTest;
65 this.statisticsModel = loadTest.getStatisticsModel();
66 this.data = statisticsModel.getHistory().getStatisticsValueHistory( Statistic.AVERAGE );
67
68 setAutoscrolls(true);
69 addMouseMotionListener(new InternalMouseMotionListener());
70
71 data.addTableModelListener(tableModelListener);
72
73 initMaxValues();
74 initScales();
75
76 setBackground( Color.WHITE );
77 setOpaque( true );
78
79 addComponentListener( new ComponentAdapter() {
80
81 public void componentResized(ComponentEvent e)
82 {
83 initScales();
84 }
85 } );
86 }
87
88 public long getResolution()
89 {
90 return statisticsModel.getHistory().getResolution();
91 }
92
93 public void setResolution( long resolution )
94 {
95 statisticsModel.getHistory().setResolution( resolution );
96 }
97
98 public TableModel getModel()
99 {
100 return data;
101 }
102
103 @Override
104 public void removeNotify()
105 {
106 super.removeNotify();
107 data.removeTableModelListener( tableModelListener );
108 }
109
110 @Override
111 public void addNotify()
112 {
113 super.addNotify();
114 data.addTableModelListener( tableModelListener );
115 }
116
117 public void setStatistic( Statistic statistic )
118 {
119 if (data != null)
120 {
121 data.removeTableModelListener(tableModelListener);
122 data.release();
123 }
124
125 data = statisticsModel.getHistory().getStatisticsValueHistory( statistic );
126
127 initMaxValues();
128 initScales();
129
130 data.addTableModelListener(tableModelListener);
131
132 getParent().invalidate();
133 revalidate();
134 repaint();
135 }
136
137 private void initMaxValues()
138 {
139 maxValues = new long[data.getColumnCount()];
140
141 for( int c = 0; c < data.getRowCount(); c++ )
142 {
143 for( int i = 0; i < data.getColumnCount(); i++ )
144 {
145 long value = (Long)data.getValueAt( c, i );
146 if( value > maxValues[i] )
147 maxValues[i] = value;
148 }
149 }
150 }
151
152 private void initScales()
153 {
154 scales = new float[maxValues.length];
155
156 for( int c = 0; c < maxValues.length; c++ )
157 {
158 recalcScale( c );
159 }
160 }
161
162 private boolean recalcScale( int index)
163 {
164 float scale = (index == 0 || maxValues[index] == 0) ? 1 : (float)(getHeight())/(float)(maxValues[index]+10);
165 if( scale > 1 ) scale = 1;
166
167 if( Float.compare( scale, scales[index] ) == 0 )
168 {
169 return false;
170 }
171
172 scales[index] = scale;
173 return true;
174 }
175
176 public void paintComponent(Graphics g)
177 {
178 g.setColor( getBackground() );
179
180 Rectangle clip = g.getClipBounds();
181 g.fillRect( (int)clip.getX(), (int)clip.getY(), (int)clip.getWidth(), (int)clip.getHeight() );
182
183 double right = clip.getX() + clip.getWidth();
184 int height = getHeight();
185
186 for( int c = (int) clip.getX(); c < data.getRowCount() && c < right; c++ )
187 {
188 for (int i = 0; i < data.getColumnCount(); i++)
189 {
190 if( i == 0 )
191 g.setColor( THREADCOUNT_COLOR );
192 else if( i == data.getColumnCount()-1 )
193 g.setColor( TOTAL_COLOR );
194 else
195 g.setColor( ColorPalette.getColor( loadTest.getTestCase().getTestStepAt( i-1 )) );
196
197 int yOffset = (int) ((float) ((Long) data.getValueAt(c, i)) * scales[i]);
198
199 if( clip.contains( c, height - yOffset - 1 ))
200 {
201 g.drawLine(c, height - yOffset - 1, c, height - yOffset -1);
202 }
203 }
204 }
205 }
206
207 public JComponent getLegend()
208 {
209 if (legend == null)
210 buildLegend();
211
212 return legend;
213 }
214
215 private void buildLegend()
216 {
217 ButtonBarBuilder builder = new ButtonBarBuilder();
218
219 builder.addFixed( new JLabel( "ThreadCount", createLegendIcon( THREADCOUNT_COLOR ), JLabel.LEFT ));
220 builder.addUnrelatedGap();
221 builder.addFixed( new JLabel( "Total", createLegendIcon( TOTAL_COLOR ), JLabel.LEFT ));
222 builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ));
223
224 legend = builder.getPanel();
225 }
226
227 private Icon createLegendIcon(Color color)
228 {
229 BufferedImage image = new BufferedImage( 10, 10, BufferedImage.TYPE_3BYTE_BGR );
230 Graphics g = image.getGraphics();
231 g.setColor( color );
232 g.fillRect( 1, 1, 8, 8 );
233 g.setColor( Color.DARK_GRAY );
234 g.drawRect( 0, 0, 10, 10 );
235 return new ImageIcon( image );
236 }
237
238 public Dimension getPreferredScrollableViewportSize()
239 {
240 return getPreferredSize();
241 }
242
243 public Dimension getPreferredSize()
244 {
245 int height = getHeight();
246 int width = data.getRowCount() + SCROLL_AHEAD;
247 return new Dimension( width, height);
248 }
249
250 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
251 {
252 return 1;
253 }
254
255 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
256 {
257 return 10;
258 }
259
260 public boolean getScrollableTracksViewportWidth()
261 {
262 return false;
263 }
264
265 public boolean getScrollableTracksViewportHeight()
266 {
267 return true;
268 }
269
270 private final class InternalTableModelListener implements TableModelListener
271 {
272 public synchronized void tableChanged(TableModelEvent e)
273 {
274 boolean repaint = false;
275
276 if( e.getType() == TableModelEvent.INSERT )
277 {
278 int firstRow = e.getFirstRow();
279 int lastRow = e.getLastRow();
280 int height = getHeight();
281
282 for( int c = firstRow; c <= lastRow; c++ )
283 {
284 for( int i = 0; i < data.getColumnCount(); i++ )
285 {
286 long value = (Long)data.getValueAt( c, i );
287
288 if( value > maxValues[i] )
289 {
290 maxValues[i] = value;
291 repaint = recalcScale( i );
292 }
293 }
294 }
295
296 if( !repaint )
297 {
298 Rectangle rect = new Rectangle(firstRow, 0, (lastRow-firstRow)+1, height );
299 repaint( rect );
300 }
301
302 Dimension size = getSize();
303 Rectangle r = getVisibleRect();
304
305 double x2 = r.getX() + r.getWidth();
306 if( x2 >= data.getRowCount() && x2 < data.getRowCount()+SCROLL_AHEAD)
307 {
308 scrollRectToVisible( new Rectangle(firstRow + SCROLL_AHEAD/2, 0, (lastRow-firstRow)+1, height ) );
309 }
310
311 if( !repaint && size.getWidth() < data.getRowCount() + SCROLL_AHEAD )
312 {
313 revalidate();
314 }
315 }
316 else if( e.getType() == TableModelEvent.UPDATE )
317 {
318 initMaxValues();
319 initScales();
320
321 repaint = true;
322 }
323
324 if( repaint )
325 {
326 getParent().invalidate();
327 revalidate();
328 repaint();
329 }
330 }
331 }
332
333 private class InternalMouseMotionListener implements MouseMotionListener
334 {
335 public void mouseDragged(MouseEvent e)
336 {
337 Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
338 scrollRectToVisible(r);
339 }
340
341 public void mouseMoved(MouseEvent e)
342 {
343 }
344 }
345 }