View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2009 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.testcase;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Color;
17  import java.awt.Component;
18  import java.awt.Font;
19  import java.awt.event.ActionEvent;
20  import java.awt.event.MouseAdapter;
21  import java.awt.event.MouseEvent;
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.PrintWriter;
25  import java.util.HashSet;
26  import java.util.Set;
27  
28  import javax.swing.AbstractAction;
29  import javax.swing.Action;
30  import javax.swing.BorderFactory;
31  import javax.swing.JLabel;
32  import javax.swing.JList;
33  import javax.swing.JPanel;
34  import javax.swing.JPopupMenu;
35  import javax.swing.JScrollPane;
36  import javax.swing.ListCellRenderer;
37  
38  import com.eviware.soapui.impl.wsdl.testcase.TestCaseLogItem;
39  import com.eviware.soapui.impl.wsdl.testcase.TestCaseLogModel;
40  import com.eviware.soapui.model.settings.Settings;
41  import com.eviware.soapui.model.testsuite.TestStepResult;
42  import com.eviware.soapui.model.testsuite.TestStepResult.TestStepStatus;
43  import com.eviware.soapui.support.StringUtils;
44  import com.eviware.soapui.support.UISupport;
45  import com.eviware.soapui.support.action.swing.ActionList;
46  import com.eviware.soapui.support.action.swing.ActionSupport;
47  import com.eviware.soapui.support.components.JHyperlinkLabel;
48  import com.eviware.soapui.support.components.JXToolBar;
49  import com.eviware.x.form.XFormDialog;
50  import com.eviware.x.form.support.ADialogBuilder;
51  import com.eviware.x.form.support.AField;
52  import com.eviware.x.form.support.AForm;
53  import com.eviware.x.form.support.AField.AFieldType;
54  
55  /***
56   * Panel for displaying TestStepResults
57   * 
58   * @author Ole.Matzura
59   */
60  
61  public class JTestRunLog extends JPanel implements TestRunLog
62  {
63  	private TestCaseLogModel logListModel;
64  	private JList testLogList;
65  	private boolean errorsOnly = false;
66  	private final Settings settings;
67  	private Set<String> boldTexts = new HashSet<String>();
68  	private boolean follow = true;
69  	protected int selectedIndex;
70  	private XFormDialog optionsDialog;
71  
72  	public JTestRunLog( Settings settings )
73  	{
74  		super( new BorderLayout() );
75  		this.settings = settings;
76  
77  		errorsOnly = settings.getBoolean( OptionsForm.class.getName() + "@errors_only" );
78  
79  		buildUI();
80  	}
81  
82  	private void buildUI()
83  	{
84  		logListModel = new TestCaseLogModel();
85  		logListModel.setMaxSize( ( int )settings.getLong( OptionsForm.class.getName() + "@max_rows", 1000 ) );
86  
87  		testLogList = new JList( logListModel );
88  		testLogList.setCellRenderer( new TestLogCellRenderer() );
89  		testLogList.setPrototypeCellValue( "Testing 123" );
90  		testLogList.setFixedCellWidth( -1 );
91  		testLogList.addMouseListener( new LogListMouseListener() );
92  
93  		JScrollPane scrollPane = new JScrollPane( testLogList );
94  		add( scrollPane, BorderLayout.CENTER );
95  		add( buildToolbar(), BorderLayout.NORTH );
96  	}
97  
98  	private Component buildToolbar()
99  	{
100 		JXToolBar toolbar = UISupport.createSmallToolbar();
101 
102 		addToolbarButtons( toolbar );
103 
104 		return toolbar;
105 	}
106 
107 	protected JList getTestLogList()
108 	{
109 		return testLogList;
110 	}
111 
112 	public boolean isErrorsOnly()
113 	{
114 		return errorsOnly;
115 	}
116 
117 	public boolean isFollow()
118 	{
119 		return follow;
120 	}
121 
122 	protected void addToolbarButtons( JXToolBar toolbar )
123 	{
124 		toolbar.addFixed( UISupport.createToolbarButton( new ClearLogAction() ) );
125 		toolbar.addFixed( UISupport.createToolbarButton( new SetLogOptionsAction() ) );
126 		toolbar.addFixed( UISupport.createToolbarButton( new ExportLogAction() ) );
127 	}
128 
129 	private final class TestLogCellRenderer extends JLabel implements ListCellRenderer
130 	{
131 		private Font boldFont;
132 		private Font normalFont;
133 		private JHyperlinkLabel hyperlinkLabel = new JHyperlinkLabel( "" );
134 
135 		public TestLogCellRenderer()
136 		{
137 			setOpaque( true );
138 			setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
139 			setIcon( null );
140 			boldFont = getFont().deriveFont( Font.BOLD );
141 			normalFont = getFont();
142 
143 			hyperlinkLabel.setOpaque( true );
144 			hyperlinkLabel.setForeground( Color.BLUE.darker().darker().darker() );
145 			hyperlinkLabel.setUnderlineColor( Color.GRAY );
146 			hyperlinkLabel.setBorder( BorderFactory.createEmptyBorder( 0, 4, 3, 3 ) );
147 		}
148 
149 		public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected,
150 				boolean cellHasFocus )
151 		{
152 			if( isSelected )
153 			{
154 				setBackground( list.getSelectionBackground() );
155 				setForeground( list.getSelectionForeground() );
156 			}
157 			else
158 			{
159 				setBackground( list.getBackground() );
160 				setForeground( list.getForeground() );
161 			}
162 
163 			if( value instanceof String )
164 			{
165 				setText( value.toString() );
166 			}
167 			else if( value instanceof TestCaseLogItem )
168 			{
169 				TestCaseLogItem logItem = ( TestCaseLogItem )value;
170 				String msg = logItem.getMsg();
171 				setText( msg == null ? "" : msg );
172 			}
173 
174 			TestStepResult result = logListModel.getResultAt( index );
175 			if( result != null && !result.isDiscarded() && result.getActions() != null && !getText().startsWith( " ->" ) )
176 			{
177 				hyperlinkLabel.setText( getText() );
178 				hyperlinkLabel.setBackground( getBackground() );
179 				hyperlinkLabel.setEnabled( list.isEnabled() );
180 
181 				if( result.getStatus() == TestStepStatus.OK )
182 				{
183 					hyperlinkLabel.setIcon( UISupport.createImageIcon( "/valid_assertion.gif" ) );
184 				}
185 				else if( result.getStatus() == TestStepStatus.FAILED )
186 				{
187 					hyperlinkLabel.setIcon( UISupport.createImageIcon( "/failed_assertion.gif" ) );
188 				}
189 				else
190 				{
191 					hyperlinkLabel.setIcon( UISupport.createImageIcon( "/unknown_assertion.gif" ) );
192 				}
193 
194 				return hyperlinkLabel;
195 			}
196 
197 			setEnabled( list.isEnabled() );
198 
199 			if( boldTexts.contains( getText() ) )
200 			{
201 				setFont( boldFont );
202 			}
203 			else
204 			{
205 				setFont( normalFont );
206 			}
207 
208 			return this;
209 		}
210 	}
211 
212 	/***
213 	 * Mouse Listener for triggering default action and showing popup for log
214 	 * list items
215 	 * 
216 	 * @author Ole.Matzura
217 	 */
218 
219 	private final class LogListMouseListener extends MouseAdapter
220 	{
221 		public void mouseClicked( MouseEvent e )
222 		{
223 			int index = testLogList.getSelectedIndex();
224 			if( index != -1 && ( index == selectedIndex || e.getClickCount() > 1 ) )
225 			{
226 				TestStepResult result = logListModel.getResultAt( index );
227 				if( result != null && result.getActions() != null )
228 					result.getActions().performDefaultAction( new ActionEvent( this, 0, null ) );
229 			}
230 
231 			selectedIndex = index;
232 		}
233 
234 		public void mousePressed( MouseEvent e )
235 		{
236 			if( e.isPopupTrigger() )
237 				showPopup( e );
238 		}
239 
240 		public void mouseReleased( MouseEvent e )
241 		{
242 			if( e.isPopupTrigger() )
243 				showPopup( e );
244 		}
245 
246 		public void showPopup( MouseEvent e )
247 		{
248 			int row = testLogList.locationToIndex( e.getPoint() );
249 			if( row == -1 )
250 				return;
251 
252 			if( testLogList.getSelectedIndex() != row )
253 			{
254 				testLogList.setSelectedIndex( row );
255 			}
256 
257 			TestStepResult result = logListModel.getResultAt( row );
258 			if( result == null )
259 				return;
260 
261 			ActionList actions = result.getActions();
262 
263 			if( actions == null || actions.getActionCount() == 0 )
264 				return;
265 
266 			JPopupMenu popup = ActionSupport.buildPopup( actions );
267 			UISupport.showPopup( popup, testLogList, e.getPoint() );
268 		}
269 	}
270 
271 	/*
272 	 * (non-Javadoc)
273 	 * 
274 	 * @see com.eviware.soapui.impl.wsdl.panels.testcase.TestRunLog#clear()
275 	 */
276 	public synchronized void clear()
277 	{
278 		logListModel.clear();
279 		boldTexts.clear();
280 	}
281 
282 	/*
283 	 * (non-Javadoc)
284 	 * 
285 	 * @see
286 	 * com.eviware.soapui.impl.wsdl.panels.testcase.TestRunLog#addText(java.lang
287 	 * .String)
288 	 */
289 	public synchronized void addText( String string )
290 	{
291 		logListModel.addText( string );
292 		if( follow )
293 			testLogList.ensureIndexIsVisible( logListModel.getSize() - 1 );
294 	}
295 
296 	/*
297 	 * (non-Javadoc)
298 	 * 
299 	 * @see
300 	 * com.eviware.soapui.impl.wsdl.panels.testcase.TestRunLog#addTestStepResult
301 	 * (com.eviware.soapui.model.testsuite.TestStepResult)
302 	 */
303 	public synchronized void addTestStepResult( TestStepResult stepResult )
304 	{
305 		if( errorsOnly && stepResult.getStatus() != TestStepResult.TestStepStatus.FAILED )
306 			return;
307 
308 		logListModel.addTestStepResult( stepResult );
309 		if( follow )
310 		{
311 			try
312 			{
313 				testLogList.ensureIndexIsVisible( logListModel.getSize() - 1 );
314 			}
315 			catch( RuntimeException e )
316 			{
317 			}
318 		}
319 	}
320 
321 	public TestCaseLogModel getLogListModel()
322 	{
323 		return logListModel;
324 	}
325 
326 	public void setLogListModel( TestCaseLogModel logListModel )
327 	{
328 		this.logListModel = logListModel;
329 		testLogList.setModel( logListModel );
330 	}
331 
332 	private class SetLogOptionsAction extends AbstractAction
333 	{
334 		public SetLogOptionsAction()
335 		{
336 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/options.gif" ) );
337 			putValue( Action.SHORT_DESCRIPTION, "Sets TestCase Log Options" );
338 		}
339 
340 		public void actionPerformed( ActionEvent e )
341 		{
342 			if( optionsDialog == null )
343 				optionsDialog = ADialogBuilder.buildDialog( OptionsForm.class );
344 
345 			optionsDialog.setIntValue( OptionsForm.MAXROWS, ( int )settings.getLong( OptionsForm.class.getName()
346 					+ "@max_rows", 1000 ) );
347 			optionsDialog.setBooleanValue( OptionsForm.ERRORSONLY, settings.getBoolean( OptionsForm.class.getName()
348 					+ "@errors_only" ) );
349 			optionsDialog.setBooleanValue( OptionsForm.FOLLOW, follow );
350 
351 			if( optionsDialog.show() )
352 			{
353 				int maxRows = optionsDialog.getIntValue( OptionsForm.MAXROWS, 1000 );
354 				logListModel.setMaxSize( maxRows );
355 				settings.setLong( OptionsForm.class.getName() + "@max_rows", maxRows );
356 				errorsOnly = optionsDialog.getBooleanValue( OptionsForm.ERRORSONLY );
357 				settings.setBoolean( OptionsForm.class.getName() + "@errors_only", errorsOnly );
358 
359 				follow = optionsDialog.getBooleanValue( OptionsForm.FOLLOW );
360 			}
361 		}
362 	}
363 
364 	@AForm( name = "Log Options", description = "Set options for the run log below" )
365 	private static interface OptionsForm
366 	{
367 		@AField( name = "Max Rows", description = "Sets the maximum number of rows to keep in the log", type = AFieldType.INT )
368 		public static final String MAXROWS = "Max Rows";
369 
370 		@AField( name = "Errors Only", description = "Logs only TestStep errors in the log", type = AFieldType.BOOLEAN )
371 		public static final String ERRORSONLY = "Errors Only";
372 
373 		@AField( name = "Follow", description = "Follow log content", type = AFieldType.BOOLEAN )
374 		public static final String FOLLOW = "Follow";
375 	}
376 
377 	private class ClearLogAction extends AbstractAction
378 	{
379 		public ClearLogAction()
380 		{
381 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_loadtest.gif" ) );
382 			putValue( Action.SHORT_DESCRIPTION, "Clears the log" );
383 		}
384 
385 		public void actionPerformed( ActionEvent e )
386 		{
387 			logListModel.clear();
388 		}
389 	}
390 
391 	private class ExportLogAction extends AbstractAction
392 	{
393 		public ExportLogAction()
394 		{
395 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/export.gif" ) );
396 			putValue( Action.SHORT_DESCRIPTION, "Exports this log to a file" );
397 		}
398 
399 		public void actionPerformed( ActionEvent e )
400 		{
401 			File file = UISupport.getFileDialogs().saveAs( this, "Save Log" );
402 			if( file != null )
403 			{
404 				try
405 				{
406 					PrintWriter out = new PrintWriter( file );
407 					printLog( out );
408 
409 					out.close();
410 				}
411 				catch( FileNotFoundException e1 )
412 				{
413 					UISupport.showErrorMessage( e1 );
414 				}
415 			}
416 		}
417 	}
418 
419 	public void setStepIndex( int i )
420 	{
421 		logListModel.setStepIndex( i );
422 	}
423 
424 	public synchronized void addBoldText( String string )
425 	{
426 		boldTexts.add( string );
427 		addText( string );
428 	}
429 
430 	public void release()
431 	{
432 		if( optionsDialog != null )
433 		{
434 			optionsDialog.release();
435 			optionsDialog = null;
436 		}
437 	}
438 
439 	public void printLog( PrintWriter out )
440 	{
441 		for( int c = 0; c < logListModel.getSize(); c++ )
442 		{
443 			Object value = logListModel.getElementAt( c );
444 			if( value instanceof String )
445 			{
446 				out.println( value.toString() );
447 			}
448 			else if( value instanceof TestCaseLogItem )
449 			{
450 				TestCaseLogItem logItem = ( TestCaseLogItem )value;
451 				String msg = logItem.getMsg();
452 				if( StringUtils.hasContent( msg ) )
453 					out.println( msg );
454 			}
455 		}
456 	}
457 }