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
82 public void componentResized( ComponentEvent e )
83 {
84 initScales();
85 }
86 } );
87 }
88
89 public long getResolution()
90 {
91 return statisticsModel.getHistory().getResolution();
92 }
93
94 public void setResolution( long resolution )
95 {
96 statisticsModel.getHistory().setResolution( resolution );
97 }
98
99 public TableModel getModel()
100 {
101 return data;
102 }
103
104 public void release()
105 {
106 data.removeTableModelListener( tableModelListener );
107 }
108
109 public void setStatistic( Statistic statistic )
110 {
111 if( data != null )
112 {
113 data.removeTableModelListener( tableModelListener );
114 data.release();
115 }
116
117 data = statisticsModel.getHistory().getStatisticsValueHistory( statistic );
118
119 initMaxValues();
120 initScales();
121
122 data.addTableModelListener( tableModelListener );
123
124 getParent().invalidate();
125 revalidate();
126 repaint();
127 }
128
129 private void initMaxValues()
130 {
131 maxValues = new long[data.getColumnCount()];
132
133 for( int c = 0; c < data.getRowCount(); c++ )
134 {
135 for( int i = 0; i < data.getColumnCount(); i++ )
136 {
137 long value = ( Long )data.getValueAt( c, i );
138 if( value > maxValues[i] )
139 maxValues[i] = value;
140 }
141 }
142 }
143
144 private void initScales()
145 {
146 scales = new float[maxValues.length];
147
148 for( int c = 0; c < maxValues.length; c++ )
149 {
150 recalcScale( c );
151 }
152 }
153
154 private boolean recalcScale( int index )
155 {
156 float scale = ( index == 0 || maxValues[index] == 0 ) ? 1 : ( float )( getHeight() )
157 / ( float )( maxValues[index] + 10 );
158 if( scale > 1 )
159 scale = 1;
160
161 if( Float.compare( scale, scales[index] ) == 0 )
162 {
163 return false;
164 }
165
166 scales[index] = scale;
167 return true;
168 }
169
170 public void paintComponent( Graphics g )
171 {
172 g.setColor( getBackground() );
173
174 Rectangle clip = g.getClipBounds();
175 g.fillRect( ( int )clip.getX(), ( int )clip.getY(), ( int )clip.getWidth(), ( int )clip.getHeight() );
176
177 double right = clip.getX() + clip.getWidth();
178 int height = getHeight();
179
180 for( int c = ( int )clip.getX(); c < data.getRowCount() && c < right; c++ )
181 {
182 for( int i = 0; i < data.getColumnCount(); i++ )
183 {
184 if( i == 0 )
185 g.setColor( THREADCOUNT_COLOR );
186 else if( i == data.getColumnCount() - 1 )
187 g.setColor( TOTAL_COLOR );
188 else
189 g.setColor( ColorPalette.getColor( loadTest.getTestCase().getTestStepAt( i - 1 ) ) );
190
191 int yOffset = ( int )( ( float )( ( Long )data.getValueAt( c, i ) ) * scales[i] );
192
193 if( clip.contains( c, height - yOffset - 1 ) )
194 {
195 g.drawLine( c, height - yOffset - 1, c, height - yOffset - 1 );
196 }
197 }
198 }
199 }
200
201 public JComponent getLegend()
202 {
203 if( legend == null )
204 buildLegend();
205
206 return legend;
207 }
208
209 private void buildLegend()
210 {
211 ButtonBarBuilder builder = new ButtonBarBuilder();
212
213 builder.addFixed( new JLabel( "ThreadCount", createLegendIcon( THREADCOUNT_COLOR ), JLabel.LEFT ) );
214 builder.addUnrelatedGap();
215 builder.addFixed( new JLabel( "Total", createLegendIcon( TOTAL_COLOR ), JLabel.LEFT ) );
216 builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
217
218 legend = builder.getPanel();
219 }
220
221 private Icon createLegendIcon( Color color )
222 {
223 BufferedImage image = new BufferedImage( 10, 10, BufferedImage.TYPE_3BYTE_BGR );
224 Graphics g = image.getGraphics();
225 g.setColor( color );
226 g.fillRect( 1, 1, 8, 8 );
227 g.setColor( Color.DARK_GRAY );
228 g.drawRect( 0, 0, 10, 10 );
229 return new ImageIcon( image );
230 }
231
232 public Dimension getPreferredScrollableViewportSize()
233 {
234 return getPreferredSize();
235 }
236
237 public Dimension getPreferredSize()
238 {
239 int height = getHeight();
240 int width = data.getRowCount() + SCROLL_AHEAD;
241 return new Dimension( width, height );
242 }
243
244 public int getScrollableUnitIncrement( Rectangle visibleRect, int orientation, int direction )
245 {
246 return 1;
247 }
248
249 public int getScrollableBlockIncrement( Rectangle visibleRect, int orientation, int direction )
250 {
251 return 10;
252 }
253
254 public boolean getScrollableTracksViewportWidth()
255 {
256 return false;
257 }
258
259 public boolean getScrollableTracksViewportHeight()
260 {
261 return true;
262 }
263
264 private final class InternalTableModelListener implements TableModelListener
265 {
266 public synchronized void tableChanged( TableModelEvent e )
267 {
268 boolean repaint = false;
269
270 if( e.getType() == TableModelEvent.INSERT )
271 {
272 int firstRow = e.getFirstRow();
273 int lastRow = e.getLastRow();
274 int height = getHeight();
275
276 for( int c = firstRow; c <= lastRow; c++ )
277 {
278 for( int i = 0; i < data.getColumnCount(); i++ )
279 {
280 long value = ( Long )data.getValueAt( c, i );
281
282 if( value > maxValues[i] )
283 {
284 maxValues[i] = value;
285 repaint = recalcScale( i );
286 }
287 }
288 }
289
290 if( !repaint )
291 {
292 Rectangle rect = new Rectangle( firstRow, 0, ( lastRow - firstRow ) + 1, height );
293 repaint( rect );
294 }
295
296 Dimension size = getSize();
297 Rectangle r = getVisibleRect();
298
299 double x2 = r.getX() + r.getWidth();
300 if( x2 >= data.getRowCount() && x2 < data.getRowCount() + SCROLL_AHEAD )
301 {
302 scrollRectToVisible( new Rectangle( firstRow + SCROLL_AHEAD / 2, 0, ( lastRow - firstRow ) + 1, height ) );
303 }
304
305 if( !repaint && size.getWidth() < data.getRowCount() + SCROLL_AHEAD )
306 {
307 revalidate();
308 }
309 }
310 else if( e.getType() == TableModelEvent.UPDATE )
311 {
312 initMaxValues();
313 initScales();
314
315 repaint = true;
316 }
317
318 if( repaint )
319 {
320 getParent().invalidate();
321 revalidate();
322 repaint();
323 }
324 }
325 }
326
327 private class InternalMouseMotionListener implements MouseMotionListener
328 {
329 public void mouseDragged( MouseEvent e )
330 {
331 Rectangle r = new Rectangle( e.getX(), e.getY(), 1, 1 );
332 scrollRectToVisible( r );
333 }
334
335 public void mouseMoved( MouseEvent e )
336 {
337 }
338 }
339 }