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.teststeps;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Component;
17  import java.awt.Dimension;
18  import java.awt.GridLayout;
19  import java.awt.event.ActionEvent;
20  import java.awt.event.ItemEvent;
21  import java.awt.event.ItemListener;
22  import java.beans.PropertyChangeEvent;
23  import java.beans.PropertyChangeListener;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Date;
27  import java.util.List;
28  
29  import javax.swing.AbstractAction;
30  import javax.swing.Action;
31  import javax.swing.BorderFactory;
32  import javax.swing.DefaultComboBoxModel;
33  import javax.swing.DefaultListCellRenderer;
34  import javax.swing.DefaultListModel;
35  import javax.swing.JButton;
36  import javax.swing.JCheckBox;
37  import javax.swing.JComboBox;
38  import javax.swing.JComponent;
39  import javax.swing.JLabel;
40  import javax.swing.JList;
41  import javax.swing.JPanel;
42  import javax.swing.JScrollPane;
43  import javax.swing.JSplitPane;
44  import javax.swing.JTextArea;
45  import javax.swing.ListSelectionModel;
46  import javax.swing.event.ChangeEvent;
47  import javax.swing.event.ChangeListener;
48  import javax.swing.event.ListSelectionEvent;
49  import javax.swing.event.ListSelectionListener;
50  import javax.swing.table.AbstractTableModel;
51  import javax.swing.text.Document;
52  
53  import org.jdesktop.swingx.JXTable;
54  
55  import com.eviware.soapui.SoapUI;
56  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
57  import com.eviware.soapui.impl.wsdl.panels.support.TestRunComponentEnabler;
58  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
59  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
60  import com.eviware.soapui.impl.wsdl.teststeps.PropertyTransfer;
61  import com.eviware.soapui.impl.wsdl.teststeps.TransferResponseValuesTestStep;
62  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequest;
63  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
64  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestStep;
65  import com.eviware.soapui.impl.wsdl.teststeps.TransferResponseValuesTestStep.ValueTransferResult;
66  import com.eviware.soapui.model.ModelItem;
67  import com.eviware.soapui.model.TestModelItem;
68  import com.eviware.soapui.model.TestPropertyHolder;
69  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
70  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
71  import com.eviware.soapui.model.support.TestRunListenerAdapter;
72  import com.eviware.soapui.model.support.TestSuiteListenerAdapter;
73  import com.eviware.soapui.model.testsuite.TestProperty;
74  import com.eviware.soapui.model.testsuite.TestPropertyListener;
75  import com.eviware.soapui.model.testsuite.TestRunContext;
76  import com.eviware.soapui.model.testsuite.TestRunner;
77  import com.eviware.soapui.model.testsuite.TestStep;
78  import com.eviware.soapui.model.testsuite.TestStepResult;
79  import com.eviware.soapui.support.DocumentListenerAdapter;
80  import com.eviware.soapui.support.UISupport;
81  import com.eviware.soapui.support.components.JComponentInspector;
82  import com.eviware.soapui.support.components.JInspectorPanel;
83  import com.eviware.soapui.support.components.JUndoableTextArea;
84  import com.eviware.soapui.support.components.JXToolBar;
85  import com.eviware.soapui.support.xml.XmlUtils;
86  import com.eviware.soapui.ui.desktop.DesktopPanel;
87  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
88  
89  /***
90   * DesktopPanel for TransferResponseValuesTestStep
91   * 
92   * @author Ole.Matzura
93   */
94  
95  public class TransferResponseValuesDesktopPanel extends ModelItemDesktopPanel<TransferResponseValuesTestStep>
96  {
97  	private final TransferResponseValuesTestStep transferStep;
98  	private DefaultListModel listModel;
99  	private JList transferList;
100 	private JTextArea sourceArea;
101 	private JTextArea targetArea;
102 	private JButton copyButton;
103 	private JButton deleteButton;
104 	private JButton declareButton;
105 	private JComboBox sourcePropertyCombo;
106 	private JComboBox targetPropertyCombo;
107 	private JComboBox sourceStepCombo;
108 	private JComboBox targetStepCombo;
109 	private DefaultComboBoxModel sourceStepModel;
110 	private DefaultComboBoxModel targetStepModel;
111 	private TestStepPropertiesListener sourceStepPropertiesListener;
112 	private TestStepPropertiesListener targetStepPropertiesListener;
113 	private TransferPropertyChangeListener transferPropertyChangeListener = new TransferPropertyChangeListener();
114 	private boolean selecting;
115 	private InternalTestSuiteListener testSuiteListener;
116 	private TestRunComponentEnabler componentEnabler;
117 	private JCheckBox failTransferCheckBox;
118 	private JButton runButton;
119 	private JButton renameButton;
120 	private JCheckBox setNullCheckBox;
121 	private JCheckBox transferTextContentCheckBox;
122 	private JCheckBox ignoreEmptyCheckBox;
123 	private JCheckBox transferAllCheckBox;
124 	private DesktopPanel resultDesktopPanel;
125 	private TransfersTableModel transfersTableModel;
126 	private InternalTestRunListener testRunListener;
127 	private JComponentInspector logInspector;
128 	private JCheckBox useXQueryCheckBox;
129 	private JButton runAllButton;
130 	private JInspectorPanel inspectorPanel;
131 
132 	public TransferResponseValuesDesktopPanel(TransferResponseValuesTestStep testStep)
133 	{
134 		super( testStep );
135 		this.transferStep = testStep;
136 		componentEnabler = new TestRunComponentEnabler( testStep.getTestCase() );
137 		
138 		buildUI();
139 		
140 		testSuiteListener = new InternalTestSuiteListener();
141 		transferStep.getTestCase().getTestSuite().addTestSuiteListener( testSuiteListener );
142 		
143 		testRunListener = new InternalTestRunListener();
144 		transferStep.getTestCase().addTestRunListener( testRunListener );
145 	}
146 
147 	protected void buildUI()
148 	{
149 		JSplitPane splitPane = UISupport.createHorizontalSplit();
150 		
151 		listModel = new DefaultListModel();
152 		
153 		for( int c = 0; c < transferStep.getTransferCount(); c++ )
154 		{
155 			listModel.addElement( transferStep.getTransferAt( c  ).getName() );
156 		}
157 		
158 		transferList = new JList( listModel );
159 		transferList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
160 		transferList.addListSelectionListener( new TransferListSelectionListener());
161 		componentEnabler.add( transferList );
162 		
163 		JScrollPane listScrollPane = new JScrollPane( transferList );
164 		UISupport.addTitledBorder( listScrollPane, "Transfers" );
165 		
166 		JPanel p = new JPanel( new BorderLayout() );
167 		p.add( listScrollPane, BorderLayout.CENTER );
168 		p.add( createPropertiesToolbar(), BorderLayout.NORTH );
169 
170 		splitPane.setLeftComponent( p );
171 		
172 		JSplitPane innerSplit = UISupport.createVerticalSplit();
173 		innerSplit.setBorder( null );
174 		sourceArea = new JUndoableTextArea();
175 		sourceArea.setToolTipText( "XPath selection from source property" );
176 		sourceArea.setEnabled( false );
177 		sourceArea.getDocument().addDocumentListener( new SourceAreaDocumentListener());
178 		componentEnabler.add( sourceArea );
179 		
180 		targetArea = new JUndoableTextArea();
181 		targetArea.setToolTipText( "XPath target in target property" );
182 		targetArea.setEnabled( false );
183 		targetArea.getDocument().addDocumentListener( new TargetAreaDocumentListener());
184 		componentEnabler.add( targetArea );
185 		
186 		JPanel sourcePanel = new JPanel( new BorderLayout() );
187 		sourcePanel.add( new JScrollPane( sourceArea ), BorderLayout.CENTER );
188 		JXToolBar toolbar = createSourceToolbar();
189 		sourcePanel.add( toolbar, BorderLayout.NORTH );
190 		sourcePanel.setBorder( BorderFactory.createEmptyBorder( 0, 3, 3, 3 ));
191 		innerSplit.setTopComponent( sourcePanel );
192 		
193 		JPanel targetPanel = new JPanel( new BorderLayout() );
194 		targetPanel.add( new JScrollPane( targetArea ), BorderLayout.CENTER );
195 		toolbar = createTargetToolbar();
196 		targetPanel.add( toolbar, BorderLayout.NORTH );
197 		targetPanel.setBorder( BorderFactory.createEmptyBorder( 0, 3, 3, 3 ));
198 		
199 		innerSplit.setBottomComponent( targetPanel );
200 		
201 		innerSplit.setResizeWeight( 0.5 );
202 		innerSplit.setDividerLocation( 0.5 );
203 		
204 		JPanel panel = createTransferOptions();
205 		
206 		JPanel innerPanel = new JPanel( new BorderLayout() );
207 		innerPanel.add( innerSplit, BorderLayout.CENTER );
208 		innerPanel.add( panel, BorderLayout.SOUTH );
209 		innerPanel.add( createConfigToolbar(), BorderLayout.NORTH );
210 		
211 		splitPane.setRightComponent( innerPanel );
212 		splitPane.setResizeWeight( 0.1 );
213 		splitPane.setDividerLocation( 120 );
214 		
215 		inspectorPanel = new JInspectorPanel( splitPane );
216 		logInspector = new JComponentInspector( buildLog(), "Transfer Log (0)", "A log of performed transfers while the editor was open", true );
217 		inspectorPanel.addInspector( logInspector );
218 		add( inspectorPanel, BorderLayout.CENTER );
219 		
220 		setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ));
221 		setPreferredSize( new Dimension( 550, 400 ));
222 		
223 		if( listModel.getSize() > 0 ) 
224 			transferList.setSelectedIndex( 0 );
225 		
226 		componentEnabler.add( deleteButton );
227 		componentEnabler.add( declareButton );
228 		componentEnabler.add( runButton );
229 		componentEnabler.add( runAllButton );
230 		componentEnabler.add( copyButton );
231 		componentEnabler.add( renameButton );
232 		componentEnabler.add( failTransferCheckBox );
233 		componentEnabler.add( setNullCheckBox );
234 		componentEnabler.add( transferTextContentCheckBox );
235 		componentEnabler.add( ignoreEmptyCheckBox );
236 		componentEnabler.add( transferAllCheckBox );
237 		componentEnabler.add( useXQueryCheckBox );
238 	}
239 
240 	private JComponent buildLog()
241 	{
242 		JPanel logPanel = new JPanel( new BorderLayout() );
243 		
244 		transfersTableModel = new TransfersTableModel();
245 		JXTable table = new JXTable( transfersTableModel );
246 		
247 		table.setHorizontalScrollEnabled( true );
248 		table.packAll();
249 		
250 		JXToolBar toolbar = UISupport.createSmallToolbar();
251 		toolbar.add( new ClearLogAction() );
252 		
253 		JScrollPane scrollPane = new JScrollPane( table);
254 		scrollPane.setBorder( BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ), 
255 					scrollPane.getBorder() ));
256 		
257 		logPanel.add( toolbar, BorderLayout.NORTH );
258 		logPanel.add( scrollPane, BorderLayout.CENTER );
259 		
260 		return logPanel;
261 	}
262 
263 	protected JXToolBar createPropertiesToolbar()
264 	{
265 		JXToolBar toolbar = UISupport.createSmallToolbar();
266 		toolbar.addFixed( UISupport.createToolbarButton( new AddAction() ) );
267 		deleteButton = UISupport.createToolbarButton( new DeleteAction() );
268 		deleteButton.setEnabled( false );
269 		toolbar.addFixed( deleteButton);
270 		copyButton = UISupport.createToolbarButton( new CopyAction() );
271 		copyButton.setEnabled( false );
272 		toolbar.addFixed( copyButton);
273 		renameButton = UISupport.createToolbarButton( new RenameAction() );
274 		renameButton.setEnabled( false );
275 		toolbar.addFixed( renameButton);
276 		return toolbar;
277 	}
278 	
279 	protected JXToolBar createConfigToolbar()
280 	{
281 		JXToolBar toolbar = UISupport.createToolbar();
282 		
283 		toolbar.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
284 		
285 		runButton = UISupport.createToolbarButton( new RunAction() );
286 		runButton.setEnabled( transferList.getSelectedIndex() != -1 );
287 		toolbar.addFixed( runButton);
288 		
289 		runAllButton = UISupport.createToolbarButton( new RunAllAction() );
290 		runAllButton.setEnabled( transferStep.getTransferCount() > 0 );
291 		toolbar.addFixed( runAllButton);
292 		
293 		declareButton = UISupport.createToolbarButton( new DeclareNamespacesAction() );
294 		declareButton.setEnabled( false );
295 		toolbar.addFixed( declareButton);
296 		toolbar.addGlue();
297 		toolbar.addFixed( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.TRANSFERSTEPEDITOR_HELP_URL )));
298 		return toolbar;
299 	}
300 
301 	protected JPanel createTransferOptions()
302 	{
303 		JPanel panel = new JPanel( new GridLayout( 3, 2 ));
304 		failTransferCheckBox = new JCheckBox( "Fail transfer on error", false );
305 		failTransferCheckBox.setToolTipText( "Fails the Property Transfer Step if an error occurs" );
306 		failTransferCheckBox.addChangeListener( new ChangeListener() {
307 
308 			public void stateChanged(ChangeEvent e)
309 			{
310 				PropertyTransfer currentTransfer = getCurrentTransfer();
311 				if( currentTransfer != null )
312 				{
313 					currentTransfer.setFailOnError( failTransferCheckBox.isSelected() );
314 				}
315 			}} );
316 		
317 		setNullCheckBox = new JCheckBox( "Set null on missing source", false );
318 		setNullCheckBox.setToolTipText( "Will set target to null if source is missing or null" );
319 		setNullCheckBox.addChangeListener( new ChangeListener() {
320 
321 			public void stateChanged(ChangeEvent e)
322 			{
323 				PropertyTransfer currentTransfer = getCurrentTransfer();
324 				if( currentTransfer != null )
325 				{
326 					currentTransfer.setSetNullOnMissingSource( setNullCheckBox.isSelected() );
327 				}
328 			}} );
329 		
330 		transferTextContentCheckBox = new JCheckBox( "Transfer text content", false );
331 		transferTextContentCheckBox.setToolTipText( "Will only transfer text content of source/target elements" );
332 		transferTextContentCheckBox.addChangeListener( new ChangeListener() {
333 
334 			public void stateChanged(ChangeEvent e)
335 			{
336 				PropertyTransfer currentTransfer = getCurrentTransfer();
337 				if( currentTransfer != null )
338 				{
339 					currentTransfer.setTransferTextContent( transferTextContentCheckBox.isSelected() );
340 				}
341 			}} );
342 		
343 		ignoreEmptyCheckBox = new JCheckBox( "Ignore empty/missing values", false );
344 		ignoreEmptyCheckBox.setToolTipText( "Will not transfer empty or missing values" );
345 		ignoreEmptyCheckBox.addChangeListener( new ChangeListener() {
346 
347 			public void stateChanged(ChangeEvent e)
348 			{
349 				PropertyTransfer currentTransfer = getCurrentTransfer();
350 				if( currentTransfer != null )
351 				{
352 					currentTransfer.setIgnoreEmpty( ignoreEmptyCheckBox.isSelected() );
353 				}
354 			}} );
355 		
356 		transferAllCheckBox = new JCheckBox( "Transfer to all", false );
357 		transferAllCheckBox.setToolTipText( "Will transfer to all matching target selections" );
358 		transferAllCheckBox.addChangeListener( new ChangeListener() {
359 
360 			public void stateChanged(ChangeEvent e)
361 			{
362 				PropertyTransfer currentTransfer = getCurrentTransfer();
363 				if( currentTransfer != null )
364 				{
365 					currentTransfer.setTransferToAll( transferAllCheckBox.isSelected() );
366 				}
367 			}} );
368 		
369 		useXQueryCheckBox = new JCheckBox( "Use XQuery", false );
370 		useXQueryCheckBox.setToolTipText( "Interprets the source xpath as an XQuery expression" );
371 		useXQueryCheckBox.addChangeListener( new ChangeListener() {
372 
373 			public void stateChanged(ChangeEvent e)
374 			{
375 				PropertyTransfer currentTransfer = getCurrentTransfer();
376 				if( currentTransfer != null )
377 				{
378 					currentTransfer.setUseXQuery( useXQueryCheckBox.isSelected() );
379 				}
380 			}} );
381 		
382 		panel.add( failTransferCheckBox );
383 		panel.add( setNullCheckBox );
384 		panel.add( transferTextContentCheckBox );
385 		panel.add( ignoreEmptyCheckBox );
386 		panel.add( transferAllCheckBox );
387 		panel.add( useXQueryCheckBox );
388 		panel.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
389 		return panel;
390 	}
391 
392 	protected JXToolBar createTargetToolbar()
393 	{
394 		JXToolBar toolbar;
395 		toolbar = UISupport.createToolbar();
396 		toolbar.addSpace( 3 );
397 		toolbar.addFixed( new JLabel( "<html><b>Target:</b></html>"));
398 		toolbar.addUnrelatedGap();
399 		
400 		targetStepCombo.setSelectedItem( null );
401 		targetStepCombo.setToolTipText( "The step the value will be transferred to" );
402 		targetStepCombo.setEnabled( false );
403 		targetStepCombo.addItemListener( 
404 				new StepComboItemListener( targetPropertyCombo, targetStepPropertiesListener ) );
405 		targetStepCombo.addItemListener( new ItemListener() {
406 
407 			public void itemStateChanged(ItemEvent e)
408 			{
409 				if( e.getStateChange() == ItemEvent.SELECTED && !selecting )
410 				{
411 					TestPropertyHolder targetStep = ( TestPropertyHolder ) targetStepCombo.getSelectedItem();
412 					PropertyTransfer valueTransfer = getCurrentTransfer();
413 					
414 					if( valueTransfer != null )
415 					{
416 						String name = "";
417 						
418 						if( targetStep == PropertyExpansionUtils.getGlobalProperties() )
419 							name = PropertyExpansion.GLOBAL_REFERENCE;
420 						else if( targetStep == transferStep.getTestCase().getTestSuite().getProject() )
421 							name = PropertyExpansion.PROJECT_REFERENCE;
422 						else if( targetStep == transferStep.getTestCase().getTestSuite())
423 							name = PropertyExpansion.TESTSUITE_REFERENCE;
424 						else if( targetStep == transferStep.getTestCase() )
425 							name = PropertyExpansion.TESTCASE_REFERENCE;
426 						else
427 						   name = targetStep.getModelItem().getName();
428 						
429 						valueTransfer.setTargetStepName( name );
430 					}
431 				}
432 			}} );
433 		
434 		toolbar.add( UISupport.setFixedSize( targetStepCombo, 180, 21 ) );
435 		toolbar.addUnrelatedGap();
436 		
437 		toolbar.addFixed( new JLabel( "Property:"));
438 		toolbar.addRelatedGap();
439 		
440 		targetPropertyCombo.setToolTipText( "The property the value will be transferred to" );
441 		targetPropertyCombo.setEnabled( false );
442 		targetPropertyCombo.addItemListener( new ItemListener() {
443 
444 			public void itemStateChanged(ItemEvent e)
445 			{
446 				if( e.getStateChange() == ItemEvent.SELECTED && !selecting )
447 				{
448 					TestProperty targetProperty = (TestProperty) targetPropertyCombo.getSelectedItem();
449 					PropertyTransfer valueTransfer = getCurrentTransfer();
450 					
451 					if( valueTransfer != null )
452 					{
453 						valueTransfer.setTargetPropertyName( targetProperty.getName() );
454 					}
455 				}
456 			}} );
457 		
458 		toolbar.add( UISupport.setFixedSize( targetPropertyCombo, 180, 21 ) );
459 		toolbar.addGlue();
460 		return toolbar;
461 	}
462 
463 	protected JXToolBar createSourceToolbar()
464 	{
465 		JXToolBar toolbar = UISupport.createToolbar();
466 		toolbar.addSpace( 3 );
467 		toolbar.addFixed( new JLabel( "<html><b>Source:</b></html>"));
468 		toolbar.addUnrelatedGap();
469 
470 		sourcePropertyCombo = new JComboBox();
471 		sourceStepModel = new DefaultComboBoxModel();
472 		sourceStepCombo = new JComboBox( sourceStepModel );
473 		sourceStepCombo.setRenderer( new StepComboRenderer() );
474 		sourcePropertyCombo.setRenderer( new PropertyComboRenderer() );
475 		
476 		componentEnabler.add( sourcePropertyCombo );
477 		componentEnabler.add( sourceStepCombo );
478 		
479 		targetPropertyCombo = new JComboBox();
480 		targetStepModel = new DefaultComboBoxModel();
481 		targetStepCombo = new JComboBox( targetStepModel );
482 		targetStepCombo.setRenderer( new StepComboRenderer() );
483 		targetPropertyCombo.setRenderer( new PropertyComboRenderer() );
484 		
485 		componentEnabler.add( targetPropertyCombo );
486 		componentEnabler.add( targetStepCombo );
487 		
488 		sourceStepPropertiesListener = new TestStepPropertiesListener( sourcePropertyCombo );
489 		targetStepPropertiesListener = new TestStepPropertiesListener( targetPropertyCombo );
490 		
491 		sourceStepModel.addElement( PropertyExpansionUtils.getGlobalProperties() );
492 		sourceStepModel.addElement( transferStep.getTestCase().getTestSuite().getProject() );
493 		sourceStepModel.addElement( transferStep.getTestCase().getTestSuite() );
494 		sourceStepModel.addElement( transferStep.getTestCase() );
495 		
496 		for( int c = 0; c < transferStep.getTestCase().getTestStepCount(); c++ )
497 		{
498 			WsdlTestStep testStep = (WsdlTestStep) transferStep.getTestCase().getTestStepAt( c );
499 			if( testStep == transferStep )
500 				continue;
501 			
502 			sourceStepModel.addElement( testStep );
503 		}
504 		
505 		for( int c = 0; c < sourceStepModel.getSize(); c++ )
506 			targetStepModel.addElement( sourceStepModel.getElementAt( c ) );
507 		
508 		sourceStepCombo.setSelectedItem( null );
509 		sourceStepCombo.setToolTipText( "The step the value will be transferred from" );
510 		sourceStepCombo.setEnabled( false );
511 		sourceStepCombo.addItemListener( 
512 				new StepComboItemListener( sourcePropertyCombo, sourceStepPropertiesListener ) );
513 		sourceStepCombo.addItemListener( new ItemListener() {
514 
515 			public void itemStateChanged(ItemEvent e)
516 			{
517 				if( e.getStateChange() == ItemEvent.SELECTED && !selecting )
518 				{
519 					TestPropertyHolder sourceStep = ( TestPropertyHolder ) sourceStepCombo.getSelectedItem();
520 					PropertyTransfer valueTransfer = getCurrentTransfer();
521 					
522 					if( valueTransfer != null )
523 					{
524 						String name = "";
525 						
526 						if( sourceStep == PropertyExpansionUtils.getGlobalProperties() )
527 							name = PropertyExpansion.GLOBAL_REFERENCE;
528 						else if( sourceStep == transferStep.getTestCase().getTestSuite().getProject() )
529 							name = PropertyExpansion.PROJECT_REFERENCE;
530 						else if( sourceStep == transferStep.getTestCase().getTestSuite())
531 							name = PropertyExpansion.TESTSUITE_REFERENCE;
532 						else if( sourceStep == transferStep.getTestCase() )
533 							name = PropertyExpansion.TESTCASE_REFERENCE;
534 						else
535 						   name = sourceStep.getModelItem().getName();
536 						
537 						valueTransfer.setSourceStepName( name );
538 					}
539 				}
540 			}} );
541 		
542 		toolbar.add( UISupport.setFixedSize( sourceStepCombo, 180, 21 ));
543 		toolbar.addUnrelatedGap();
544 		
545 		toolbar.addFixed( new JLabel( "Property:"));
546 		toolbar.addRelatedGap();
547 		
548 		sourcePropertyCombo.setToolTipText( "The property the value will be transferred from" );
549 		sourcePropertyCombo.setEnabled( false );
550 		sourcePropertyCombo.addItemListener( new ItemListener() {
551 
552 			public void itemStateChanged(ItemEvent e)
553 			{
554 				if( e.getStateChange() == ItemEvent.SELECTED && !selecting )
555 				{
556 					TestProperty sourceProperty = (TestProperty) sourcePropertyCombo.getSelectedItem();
557 					PropertyTransfer valueTransfer = getCurrentTransfer();
558 					
559 					if( valueTransfer != null )
560 					{
561 						valueTransfer.setSourcePropertyName( sourceProperty.getName() );
562 					}
563 				}
564 			}} );
565 		
566 		toolbar.add( UISupport.setFixedSize( sourcePropertyCombo, 180, 21 ) );
567 		toolbar.addGlue();
568 		return toolbar;
569 	}
570 	
571 	public PropertyTransfer getCurrentTransfer()
572 	{
573 		int ix = transferList.getSelectedIndex();
574 		return ix == -1 ? null : transferStep.getTransferAt( ix );
575 	}
576 
577 	/***
578 	 * Listen for testStep property changes and update properties combo accordingly
579 	 */
580 	
581 	private final class TestStepPropertiesListener implements TestPropertyListener
582 	{
583 		private final JComboBox combo;
584 
585 		public TestStepPropertiesListener(JComboBox combo)
586 		{
587 			this.combo = combo;
588 		}
589 
590 		public void propertyAdded(String name)
591 		{
592 			TestProperty property = combo == targetPropertyCombo ?
593 				getCurrentTransfer().getTargetStep().getProperty( name ) :
594 				getCurrentTransfer().getSourceStep().getProperty( name );
595 			
596 			combo.addItem( property );
597 			combo.setEnabled( true );
598 		}
599 
600 		public void propertyRemoved(String name)
601 		{
602 			if( ((TestProperty)combo.getSelectedItem()).getName().equals( name ))
603 				combo.setSelectedItem( null );
604 			
605 			for( int c = 0; c < combo.getItemCount(); c++ )
606 			{
607 				if( ((TestProperty)combo.getItemAt( c )).getName().equals( name ))
608 				{
609 					combo.removeItemAt( c );
610 					break;
611 				}			
612 			}
613 			
614 			combo.setEnabled( combo.getItemCount() > 0 );
615 		}
616 
617 		public void propertyRenamed(String oldName, String newName)
618 		{
619 		}
620 
621 		public void propertyValueChanged(String name, String oldValue, String newValue)
622 		{
623 		}
624 	}
625 
626 	/***
627 	 * Listen for teststep changes and update source/target step combos accordingly
628 	 */
629 	
630 	private final class InternalTestSuiteListener extends TestSuiteListenerAdapter
631 	{
632 		public void testStepAdded(TestStep testStep, int index)
633 		{
634 			if( testStep.getTestCase() == transferStep.getTestCase() )
635 			{
636 				sourceStepModel.addElement( testStep );
637 				targetStepModel.addElement( testStep );
638 			}
639 		}
640 
641 		public void testStepMoved(TestStep testStep, int fromIndex, int offset)
642 		{
643 			if( testStep.getTestCase() == transferStep.getTestCase() )
644 			{
645 				String testStepName = testStep.getName();
646 				if( sourceStepModel.getIndexOf( testStepName ) == fromIndex )
647 				{
648 					String sourceStep = ( String ) sourceStepCombo.getSelectedItem();
649 					String sourceProperty = ( String ) sourcePropertyCombo.getSelectedItem();
650 					
651 					sourceStepModel.removeElementAt( fromIndex );
652 					if( fromIndex + offset > sourceStepModel.getSize() )
653 						sourceStepModel.addElement( testStepName );
654 					else 
655 						sourceStepModel.insertElementAt( testStepName, fromIndex + offset );
656 					
657 					sourceStepCombo.setSelectedItem( sourceStep );
658 					sourcePropertyCombo.setSelectedItem( sourceProperty );
659 				}
660 				
661 				if( targetStepModel.getIndexOf( testStepName ) == fromIndex )
662 				{
663 					String targetStep = ( String ) targetStepCombo.getSelectedItem();
664 					String targetProperty = ( String ) targetPropertyCombo.getSelectedItem();
665 					
666 					targetStepModel.removeElementAt( fromIndex );
667 					if( fromIndex + offset > targetStepModel.getSize() )
668 						targetStepModel.addElement( testStepName );
669 					else 
670 						targetStepModel.insertElementAt( testStepName, fromIndex + offset );
671 					
672 					targetStepCombo.setSelectedItem( targetStep );
673 					targetPropertyCombo.setSelectedItem( targetProperty );
674 				}
675 			}
676 		}
677 
678 		public void testStepRemoved(TestStep testStep, int index)
679 		{
680 			if( testStep.getTestCase() == transferStep.getTestCase() )
681 			{
682 				sourceStepModel.removeElement( testStep );
683 				targetStepModel.removeElement( testStep );
684 			}
685 		}
686 	}
687 
688 	/***
689 	 * Listen to step selections and update properties combo accordingly 
690 	 */
691 	
692 	private final class StepComboItemListener implements ItemListener
693 	{
694 		private final JComboBox propertyCombo;
695 		private final TestStepPropertiesListener testStepPropertiesListener;
696 		
697 		public StepComboItemListener(final JComboBox propertyCombo, TestStepPropertiesListener testStepPropertiesListener)
698 		{
699 			this.propertyCombo = propertyCombo;
700 			this.testStepPropertiesListener = testStepPropertiesListener;
701 		}
702 
703 		public void itemStateChanged(ItemEvent e)
704 		{
705 			if( e.getStateChange() == ItemEvent.SELECTED )
706 			{
707 				TestPropertyHolder selectedItem =  ( TestPropertyHolder ) e.getItem();
708 				String[] propertyNames = selectedItem.getPropertyNames();
709 				
710 				// remove read-only properties from target property
711 				if( propertyCombo == targetPropertyCombo )
712 				{
713 					List<String> names = new ArrayList<String>();
714 					for( String name : propertyNames )
715 					{
716 						TestProperty property = selectedItem.getProperty( name );
717 						if( property != null && !property.isReadOnly() )
718 							names.add( property.getName() );
719 					}
720 					
721 					propertyNames = names.toArray( new String[names.size()] );
722 				}
723 				
724 				DefaultComboBoxModel model = new DefaultComboBoxModel();
725 				for( String name : propertyNames )
726 					model.addElement( selectedItem.getProperty( name ) );
727 				
728 				propertyCombo.setModel( model);
729 				propertyCombo.setEnabled( propertyNames.length > 0 );
730 				
731 				if( propertyCombo == targetPropertyCombo )
732 					propertyCombo.setSelectedItem( getCurrentTransfer().getTargetProperty() );
733 				else
734 					propertyCombo.setSelectedItem( getCurrentTransfer().getSourceProperty() );
735 				
736 				selectedItem.addTestPropertyListener( testStepPropertiesListener );
737 			}
738 			else 
739 			{
740 				propertyCombo.removeAllItems();
741 				propertyCombo.setEnabled( false );
742 			}
743 		}
744 	}
745 
746 	/***
747 	 * Handle updates to source path
748 	 */
749 	
750 	private final class SourceAreaDocumentListener extends DocumentListenerAdapter
751 	{
752 		public void update(Document document)
753 		{
754 			int ix = transferList.getSelectedIndex();
755 			if( ix != -1 )
756 			{
757 				transferStep.getTransferAt( ix ).setSourcePath( sourceArea.getText() );
758 			}
759 		}
760 	}
761 	
762 	/***
763 	 * Handle updates to target path
764 	 */
765 	
766 	private final class TargetAreaDocumentListener extends DocumentListenerAdapter
767 	{
768 		public void update( Document document )
769 		{
770 			int ix = transferList.getSelectedIndex();
771 			if( ix != -1 )
772 			{
773 				transferStep.getTransferAt( ix ).setTargetPath( targetArea.getText() );
774 			}
775 		}
776 	}
777 
778 	/***
779 	 * Listen to selection changes in transfer list and update controls accordingly
780 	 */
781 	
782 	private final class TransferListSelectionListener implements ListSelectionListener
783 	{
784 		private PropertyTransfer transfer;
785 
786 		public void valueChanged(ListSelectionEvent e)
787 		{
788 			selecting = true;
789 			
790 			if( transfer != null )
791 			{
792 				transfer.removePropertyChangeListener( transferPropertyChangeListener );
793 			}
794 			
795 			transfer = getCurrentTransfer();
796 			
797 			if( transfer == null )
798 			{
799 				sourceArea.setText( "" );
800 				targetArea.setText( "" );
801 				
802 				sourcePropertyCombo.removeAllItems();
803 				targetPropertyCombo.removeAllItems();
804 				
805 				sourceStepCombo.setSelectedIndex( -1 );
806 				targetStepCombo.setSelectedIndex( -1 );
807 			}
808 			else
809 			{
810 				transfer.addPropertyChangeListener( transferPropertyChangeListener );
811 				
812 				sourceArea.setText( transfer.getSourcePath() );
813 				sourceArea.setCaretPosition( 0 );
814 				targetArea.setText( transfer.getTargetPath() );
815 				targetArea.setCaretPosition( 0 );
816 
817 				sourceStepCombo.setSelectedItem( transfer.getSourceStep() );
818 				sourcePropertyCombo.setSelectedItem( transfer.getSourceProperty() );
819 				
820 				targetStepCombo.setSelectedItem( transfer.getTargetStep() );
821 				targetPropertyCombo.setSelectedItem( transfer.getTargetProperty() );
822 				
823 				failTransferCheckBox.setSelected( transfer.getFailOnError() );
824 				setNullCheckBox.setSelected( transfer.getSetNullOnMissingSource() );
825 				transferTextContentCheckBox.setSelected( transfer.getTransferTextContent() );
826 				ignoreEmptyCheckBox.setSelected( transfer.getIgnoreEmpty() );
827 				transferAllCheckBox.setSelected( transfer.getTransferToAll() );
828 				useXQueryCheckBox.setSelected( transfer.getUseXQuery() );
829 			}
830 			
831 			copyButton.setEnabled( transfer != null );
832 			renameButton.setEnabled( transfer != null );
833 			deleteButton.setEnabled( transfer != null );
834 			declareButton.setEnabled( transfer != null );
835 			sourceStepCombo.setEnabled( transfer != null );
836 			targetStepCombo.setEnabled( transfer != null );
837 			sourceArea.setEnabled( transfer != null );
838 			targetArea.setEnabled( transfer != null );
839 			failTransferCheckBox.setEnabled( transfer != null );
840 			setNullCheckBox.setEnabled( transfer != null );
841 			transferTextContentCheckBox.setEnabled( transfer != null );
842 			ignoreEmptyCheckBox.setEnabled( transfer != null );
843 			transferAllCheckBox.setEnabled( transfer != null );
844 			useXQueryCheckBox.setEnabled( transfer != null );
845 			
846 			runAllButton.setEnabled( transferList.getModel().getSize() > 0 );
847 			runButton.setEnabled( transfer != null );
848 
849 			sourcePropertyCombo.setEnabled( transfer != null );
850 			targetPropertyCombo.setEnabled( transfer != null );
851 			
852 			selecting = false;
853 		}
854 	}
855 	
856 	/***
857 	 * Listen to property changes and update UI objects. These may have been triggered by UI so first check
858 	 * for actual difference so we dont end up in loop.
859 	 */
860 
861 	private class TransferPropertyChangeListener implements PropertyChangeListener
862 	{
863 		public void propertyChange(PropertyChangeEvent evt)
864 		{
865 			Object newValue = evt.getNewValue();
866 			
867 			if( evt.getPropertyName().equals( PropertyTransfer.SOURCE_PATH_PROPERTY ))
868 			{
869 				if( !sourceArea.getText().equals( newValue ))
870 					sourceArea.setText( (String) newValue );
871 			}
872 			else if( evt.getPropertyName().equals( PropertyTransfer.TARGET_PATH_PROPERTY ))
873 			{
874 				if( !targetArea.getText().equals( newValue ))
875 					targetArea.setText( (String) newValue );
876 			}
877 			else if( evt.getPropertyName().equals( PropertyTransfer.SOURCE_STEP_PROPERTY ))
878 			{
879 				Object selectedItem = sourceStepCombo.getSelectedItem();
880 				if( newValue == null || selectedItem == null || !selectedItem.equals( newValue ))
881 				{
882 					selecting = true;
883 					sourceStepCombo.setSelectedItem( newValue );
884 					selecting = false;
885 				}
886 			}
887 			else if( evt.getPropertyName().equals( PropertyTransfer.TARGET_STEP_PROPERTY ))
888 			{
889 				Object selectedItem = targetStepCombo.getSelectedItem();
890 				if( newValue == null || selectedItem == null || !selectedItem.equals( newValue ))
891 				{
892 					selecting = true;
893 					targetStepCombo.setSelectedItem( newValue );
894 					selecting = false;
895 				}
896 			}
897 			else if( evt.getPropertyName().equals( PropertyTransfer.SOURCE_TYPE_PROPERTY ))
898 			{
899 				Object selectedItem = sourcePropertyCombo.getSelectedItem();
900 				if( selectedItem == null || !selectedItem.equals( newValue ))
901 					sourcePropertyCombo.setSelectedItem( newValue );
902 			}
903 			else if( evt.getPropertyName().equals( PropertyTransfer.TARGET_TYPE_PROPERTY ))
904 			{
905 				Object selectedItem = targetPropertyCombo.getSelectedItem();
906 				if( selectedItem == null || !selectedItem.equals( newValue ))
907 					targetPropertyCombo.setSelectedItem( newValue );
908 			}
909 		}
910 	}
911 	
912 	private final class AddAction extends AbstractAction
913 	{
914 		public AddAction()
915 		{
916 			putValue( Action.SHORT_DESCRIPTION, "Adds a new Property Transfer" );
917 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/add_property.gif" ));
918 		}
919 		
920 		public void actionPerformed(ActionEvent e)
921 		{
922 			String name = UISupport.prompt( "Specify name for value transfer", "Add Transfer", "" );
923 			if( name == null || name.trim().length() == 0 ) return;
924 			
925 			transferStep.addTransfer( name );
926 			
927 			listModel.addElement( name );
928 			transferList.setSelectedIndex( listModel.getSize()-1 );
929 		}
930 	}
931 	
932 	private final class CopyAction extends AbstractAction
933 	{
934 		public CopyAction()
935 		{
936 			putValue( Action.SHORT_DESCRIPTION, "Copies the selected Property Transfer" );
937 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clone_request.gif" ));
938 		}
939 		
940 		public void actionPerformed(ActionEvent e)
941 		{
942 			int ix = transferList.getSelectedIndex();
943 			PropertyTransfer config = transferStep.getTransferAt( ix );
944 			
945 			String name = UISupport.prompt( "Specify name for value transfer", "Copy Transfer", config.getName() );
946 			if( name == null || name.trim().length() == 0 ) return;
947 			
948 			PropertyTransfer transfer = transferStep.addTransfer( name );
949 			transfer.setSourceStepName( config.getSourceStepName() );
950 			transfer.setSourcePropertyName( config.getSourcePropertyName() );
951 			transfer.setSourcePath( config.getSourcePath() );
952 			transfer.setTargetStepName( config.getTargetStepName() );
953 			transfer.setTargetPropertyName( config.getTargetPropertyName() );
954 			transfer.setTargetPath( config.getTargetPath() );
955 			
956 			listModel.addElement( name );
957 			transferList.setSelectedIndex( listModel.getSize()-1 );
958 		}
959 	}
960 	
961 	private final class DeleteAction extends AbstractAction
962 	{
963 		public DeleteAction()
964 		{
965 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ));
966 			putValue( Action.SHORT_DESCRIPTION, "Deletes the selected Property Transfer" );
967 		}
968 		
969 		public void actionPerformed(ActionEvent e)
970 		{
971 			if( UISupport.confirm( "Delete selected transfer", "Delete Transfer" )) 
972 			{
973 				transferList.setSelectedIndex( -1 );
974 				
975 				int ix = transferList.getSelectedIndex();
976 				transferStep.removeTransferAt( ix );
977 				listModel.remove( ix );
978 				
979 				if( listModel.getSize() > 0 )
980 				{
981 					transferList.setSelectedIndex( ix > listModel.getSize()-1 ? listModel.getSize()-1 : ix );
982 				}
983 			}
984 		}
985 	}
986 	
987 	private final class ClearLogAction extends AbstractAction
988 	{
989 		public ClearLogAction()
990 		{
991 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_properties.gif" ));
992 			putValue( Action.SHORT_DESCRIPTION, "Clears the property-transfer log" );
993 		}
994 		
995 		public void actionPerformed(ActionEvent e)
996 		{
997 			transfersTableModel.clear();
998 		}
999 	}
1000 	
1001 	private final class RenameAction extends AbstractAction
1002 	{
1003 		public RenameAction()
1004 		{
1005 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/rename.gif" ));
1006 			putValue( Action.SHORT_DESCRIPTION, "Renames the selected Property Transfer" );
1007 		}
1008 		
1009 		public void actionPerformed(ActionEvent e)
1010 		{
1011 			PropertyTransfer transfer = getCurrentTransfer();
1012 			
1013 			String newName = UISupport.prompt( "Specify new name for transfer", "Rename Transfer", transfer.getName() );
1014 			
1015 			if( newName != null && !transfer.getName().equals( newName )) 
1016 			{
1017 				listModel.setElementAt( newName, transferList.getSelectedIndex() );
1018 				transfer.setName( newName );
1019 			}
1020 		}
1021 	}
1022 	
1023 	private final class DeclareNamespacesAction extends AbstractAction
1024 	{
1025 		public DeclareNamespacesAction()
1026 		{
1027 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/declareNs.gif" ));
1028 			putValue( Action.SHORT_DESCRIPTION, "Declare available response/request namespaces in source/target expressions" );
1029 		}
1030 		
1031 		public void actionPerformed(ActionEvent e)
1032 		{
1033 			try
1034 			{
1035 				TestPropertyHolder previousStep = getCurrentTransfer().getSourceStep();
1036 				
1037 				if ( previousStep instanceof WsdlTestRequestStep )
1038 				{
1039 					WsdlTestRequest testRequest = ((WsdlTestRequestStep)previousStep).getTestRequest();
1040 					sourceArea.setText( XmlUtils.declareXPathNamespaces( testRequest.getOperation().getInterface() ) + sourceArea.getText() );
1041 				}
1042 				else UISupport.showErrorMessage( "Property Source is not a Request" );
1043 
1044 				TestPropertyHolder nextStep = getCurrentTransfer().getTargetStep();
1045 				
1046 				if (nextStep instanceof WsdlTestRequestStep)
1047 				{
1048 					WsdlTestRequest testRequest = ((WsdlTestRequestStep)nextStep).getTestRequest();
1049 					targetArea.setText( XmlUtils.declareXPathNamespaces( testRequest.getOperation().getInterface()) + targetArea.getText() );
1050 				}
1051 				else UISupport.showErrorMessage( "Property Target is not a Request" );
1052 			}
1053 			catch (Exception e1)
1054 			{
1055 				UISupport.showErrorMessage( e1 );
1056 			}
1057 		}
1058 	}
1059 	
1060 	private final class RunAllAction extends AbstractAction
1061 	{
1062 		public RunAllAction()
1063 		{
1064 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run_all.gif" ));
1065 			putValue( Action.SHORT_DESCRIPTION, "Runs all Property Transfers" );
1066 		}
1067 		
1068 		public void actionPerformed(ActionEvent e)
1069 		{
1070 			if( listModel.getSize() == 0 )
1071 			{
1072 				UISupport.showErrorMessage( "Missing transfers!" );
1073 				return;
1074 			}
1075 			
1076 			WsdlTestRunContext context = new WsdlTestRunContext( transferStep );
1077 			ValueTransferResult result = (ValueTransferResult) transferStep.run( null, context );
1078 			transfersTableModel.addResult( result );
1079 		}
1080 	}
1081 	
1082 	private final class RunAction extends AbstractAction
1083 	{
1084 		public RunAction()
1085 		{
1086 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run.gif" ));
1087 			putValue( Action.SHORT_DESCRIPTION, "Runs selected PropertyTransfer" );
1088 		}
1089 		
1090 		public void actionPerformed(ActionEvent e)
1091 		{
1092 			if( transferList.getSelectedIndex() == -1 )
1093 			{
1094 				UISupport.showErrorMessage( "No transfer selectd!" );
1095 				return;
1096 			}
1097 			
1098 			WsdlTestRunContext context = new WsdlTestRunContext( transferStep );
1099 			ValueTransferResult result = (ValueTransferResult) transferStep.run( null, context, getCurrentTransfer() );
1100 			transfersTableModel.addResult( result );
1101 		}
1102 	}
1103 	
1104 	public boolean onClose( boolean canCancel )
1105 	{
1106 		super.release();
1107 		transferStep.getTestCase().getTestSuite().removeTestSuiteListener( testSuiteListener ); 
1108 		transferStep.getTestCase().removeTestRunListener( testRunListener );
1109 		
1110 		PropertyTransfer transfer = getCurrentTransfer();
1111 		
1112 		if( transfer != null )
1113 		{
1114 			transfer.removePropertyChangeListener( transferPropertyChangeListener );
1115 		}
1116 		
1117 		TestPropertyHolder item = ( TestPropertyHolder ) sourceStepCombo.getSelectedItem();
1118 		if( item != null )
1119 		{
1120 			item.removeTestPropertyListener( sourceStepPropertiesListener );
1121 		}
1122 
1123 		item = ( TestPropertyHolder ) targetStepCombo.getSelectedItem();
1124 		if( item != null )
1125 		{
1126 			item.removeTestPropertyListener( targetStepPropertiesListener );
1127 		}
1128 
1129 		componentEnabler.release();
1130 		if( resultDesktopPanel != null )
1131 			SoapUI.getDesktop().closeDesktopPanel( resultDesktopPanel );
1132 		
1133 		return true;
1134 	}
1135 
1136 	public JComponent getComponent()
1137 	{
1138 		return this;
1139 	}
1140 	
1141 	protected JTextArea getSourceArea()
1142 	{
1143 		return sourceArea;
1144 	}
1145 
1146 	protected JTextArea getTargetArea()
1147 	{
1148 		return targetArea;
1149 	}
1150 
1151 	public boolean dependsOn(ModelItem modelItem)
1152 	{
1153 		return modelItem == transferStep || modelItem == transferStep.getTestCase() ||
1154 				modelItem == transferStep.getTestCase().getTestSuite() ||
1155 				modelItem == transferStep.getTestCase().getTestSuite().getProject();
1156 	}
1157 
1158 	public boolean selectTransfer(PropertyTransfer transfer)
1159 	{
1160 		for( int c = 0; c < transferStep.getTransferCount(); c++ )
1161 		{
1162 			if( transferStep.getTransferAt( c ) == transfer )
1163 			{
1164 				transferList.setSelectedIndex( c );
1165 				return true;
1166 			}
1167 		}
1168 		
1169 		return false;
1170 	}
1171 	
1172 	private class TransfersTableModel extends AbstractTableModel
1173 	{
1174 		private List<ValueTransferResult> results = new ArrayList<ValueTransferResult>();
1175 		
1176 		public synchronized int getRowCount()
1177 		{
1178 			int sum = 0; 
1179 			for( ValueTransferResult result : results )
1180 			{
1181 				sum += result.getTransferCount();
1182 			}
1183 			
1184 			return sum;
1185 		}
1186 
1187 		public synchronized void clear()
1188 		{
1189 			results.clear();
1190 			fireTableDataChanged();
1191 			logInspector.setTitle(  "Transfer Log (0)" );
1192 		}
1193 
1194 		public synchronized void addResult( ValueTransferResult result )
1195 		{
1196 			int rowCount = getRowCount();
1197 			results.add( result );
1198 			fireTableRowsInserted( rowCount, rowCount + result.getTransferCount() );
1199 			
1200 			logInspector.setTitle(  "Transfer Log (" + getRowCount() + ")" );
1201 			inspectorPanel.activate( logInspector );
1202 		}
1203 		
1204 		public int getColumnCount()
1205 		{
1206 			return 3;
1207 		}
1208 		
1209 		public String getColumnName(int column)
1210 		{
1211 			switch( column )
1212 			{
1213 			case 0 : return "Timestamp";
1214 			case 1 : return "Transfer Name";
1215 			case 2 : return "Transferred Values";
1216 			}
1217 			
1218 			return null;
1219 		}
1220 
1221 		public synchronized Object getValueAt(int rowIndex, int columnIndex)
1222 		{
1223 			// find correct transfer
1224 			ValueTransferResult result = null;
1225 			int sum = 0;
1226 			
1227 			for( int c = 0; c < results.size(); c++ )
1228 			{
1229 				if( sum + results.get( c ).getTransferCount() > rowIndex )
1230 				{
1231 					result = results.get( c );
1232 					break;
1233 				}
1234 				else
1235 				{
1236 					sum += results.get( c ).getTransferCount();
1237 				}
1238 			}
1239 			
1240 			if( result != null )
1241 			{
1242 				switch( columnIndex )
1243 				{
1244 					case 0 : return new Date( result.getTimeStamp() ).toString();
1245 					case 1 : return result.getTransferAt( rowIndex-sum ).getName();
1246 					case 2 : return Arrays.toString( result.getTransferredValuesAt( rowIndex-sum ) );
1247 				}
1248 			}
1249 			
1250 			return null;
1251 		}
1252 		
1253 	}
1254 	
1255 	private class InternalTestRunListener extends TestRunListenerAdapter
1256 	{
1257 		@Override
1258 		public void afterStep( TestRunner testRunner, TestRunContext runContext, TestStepResult result )
1259 		{
1260 			if( result.getTestStep() == transferStep )
1261 			{
1262 				transfersTableModel.addResult( ( ValueTransferResult ) result );
1263 			}
1264 		}
1265 	}
1266 	
1267 	private class StepComboRenderer extends DefaultListCellRenderer
1268 	{
1269 		@Override
1270 		public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus )
1271 		{
1272 			Component result = super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
1273 			
1274 			if( value instanceof TestModelItem )
1275 			{
1276 				TestModelItem item = ( TestModelItem ) value;
1277 				setIcon( item.getIcon() );
1278 				setText( item.getName() );
1279 			}
1280 			else if( value == PropertyExpansionUtils.getGlobalProperties())
1281 			{
1282 				setText( "Global" );
1283 			}
1284 			
1285 			return result;
1286 		}
1287 	}
1288 	
1289 	private class PropertyComboRenderer extends DefaultListCellRenderer
1290 	{
1291 		@Override
1292 		public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus )
1293 		{
1294 			Component result = super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
1295 			
1296 			if( value != null )
1297 			{
1298 				TestProperty item = ( TestProperty ) value;
1299 				setText( item.getName() );
1300 			}
1301 			
1302 			return result;
1303 		}
1304 	}
1305 }