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 	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 }