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