View Javadoc

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