View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
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 }