View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 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  			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 }