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 				targetArea.setText( transfer.getTargetPath() );
814 
815 				sourceStepCombo.setSelectedItem( transfer.getSourceStep() );
816 				sourcePropertyCombo.setSelectedItem( transfer.getSourceProperty() );
817 				
818 				targetStepCombo.setSelectedItem( transfer.getTargetStep() );
819 				targetPropertyCombo.setSelectedItem( transfer.getTargetProperty() );
820 				
821 				failTransferCheckBox.setSelected( transfer.getFailOnError() );
822 				setNullCheckBox.setSelected( transfer.getSetNullOnMissingSource() );
823 				transferTextContentCheckBox.setSelected( transfer.getTransferTextContent() );
824 				ignoreEmptyCheckBox.setSelected( transfer.getIgnoreEmpty() );
825 				transferAllCheckBox.setSelected( transfer.getTransferToAll() );
826 				useXQueryCheckBox.setSelected( transfer.getUseXQuery() );
827 			}
828 			
829 			copyButton.setEnabled( transfer != null );
830 			renameButton.setEnabled( transfer != null );
831 			deleteButton.setEnabled( transfer != null );
832 			declareButton.setEnabled( transfer != null );
833 			sourceStepCombo.setEnabled( transfer != null );
834 			targetStepCombo.setEnabled( transfer != null );
835 			sourceArea.setEnabled( transfer != null );
836 			targetArea.setEnabled( transfer != null );
837 			failTransferCheckBox.setEnabled( transfer != null );
838 			setNullCheckBox.setEnabled( transfer != null );
839 			transferTextContentCheckBox.setEnabled( transfer != null );
840 			ignoreEmptyCheckBox.setEnabled( transfer != null );
841 			transferAllCheckBox.setEnabled( transfer != null );
842 			useXQueryCheckBox.setEnabled( transfer != null );
843 			
844 			runAllButton.setEnabled( transferList.getModel().getSize() > 0 );
845 			runButton.setEnabled( transfer != null );
846 
847 			sourcePropertyCombo.setEnabled( transfer != null );
848 			targetPropertyCombo.setEnabled( transfer != null );
849 			
850 			selecting = false;
851 		}
852 	}
853 	
854 	/***
855 	 * Listen to property changes and update UI objects. These may have been triggered by UI so first check
856 	 * for actual difference so we dont end up in loop.
857 	 */
858 
859 	private class TransferPropertyChangeListener implements PropertyChangeListener
860 	{
861 		public void propertyChange(PropertyChangeEvent evt)
862 		{
863 			Object newValue = evt.getNewValue();
864 			
865 			if( evt.getPropertyName().equals( PropertyTransfer.SOURCE_PATH_PROPERTY ))
866 			{
867 				if( !sourceArea.getText().equals( newValue ))
868 					sourceArea.setText( (String) newValue );
869 			}
870 			else if( evt.getPropertyName().equals( PropertyTransfer.TARGET_PATH_PROPERTY ))
871 			{
872 				if( !targetArea.getText().equals( newValue ))
873 					targetArea.setText( (String) newValue );
874 			}
875 			else if( evt.getPropertyName().equals( PropertyTransfer.SOURCE_STEP_PROPERTY ))
876 			{
877 				Object selectedItem = sourceStepCombo.getSelectedItem();
878 				if( newValue == null || selectedItem == null || !selectedItem.equals( newValue ))
879 				{
880 					selecting = true;
881 					sourceStepCombo.setSelectedItem( newValue );
882 					selecting = false;
883 				}
884 			}
885 			else if( evt.getPropertyName().equals( PropertyTransfer.TARGET_STEP_PROPERTY ))
886 			{
887 				Object selectedItem = targetStepCombo.getSelectedItem();
888 				if( newValue == null || selectedItem == null || !selectedItem.equals( newValue ))
889 				{
890 					selecting = true;
891 					targetStepCombo.setSelectedItem( newValue );
892 					selecting = false;
893 				}
894 			}
895 			else if( evt.getPropertyName().equals( PropertyTransfer.SOURCE_TYPE_PROPERTY ))
896 			{
897 				Object selectedItem = sourcePropertyCombo.getSelectedItem();
898 				if( selectedItem == null || !selectedItem.equals( newValue ))
899 					sourcePropertyCombo.setSelectedItem( newValue );
900 			}
901 			else if( evt.getPropertyName().equals( PropertyTransfer.TARGET_TYPE_PROPERTY ))
902 			{
903 				Object selectedItem = targetPropertyCombo.getSelectedItem();
904 				if( selectedItem == null || !selectedItem.equals( newValue ))
905 					targetPropertyCombo.setSelectedItem( newValue );
906 			}
907 		}
908 	}
909 	
910 	private final class AddAction extends AbstractAction
911 	{
912 		public AddAction()
913 		{
914 			putValue( Action.SHORT_DESCRIPTION, "Adds a new Property Transfer" );
915 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/add_property.gif" ));
916 		}
917 		
918 		public void actionPerformed(ActionEvent e)
919 		{
920 			String name = UISupport.prompt( "Specify name for value transfer", "Add Transfer", "" );
921 			if( name == null || name.trim().length() == 0 ) return;
922 			
923 			transferStep.addTransfer( name );
924 			
925 			listModel.addElement( name );
926 			transferList.setSelectedIndex( listModel.getSize()-1 );
927 		}
928 	}
929 	
930 	private final class CopyAction extends AbstractAction
931 	{
932 		public CopyAction()
933 		{
934 			putValue( Action.SHORT_DESCRIPTION, "Copies the selected Property Transfer" );
935 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clone_request.gif" ));
936 		}
937 		
938 		public void actionPerformed(ActionEvent e)
939 		{
940 			int ix = transferList.getSelectedIndex();
941 			PropertyTransfer config = transferStep.getTransferAt( ix );
942 			
943 			String name = UISupport.prompt( "Specify name for value transfer", "Copy Transfer", config.getName() );
944 			if( name == null || name.trim().length() == 0 ) return;
945 			
946 			PropertyTransfer transfer = transferStep.addTransfer( name );
947 			transfer.setSourceStepName( config.getSourceStepName() );
948 			transfer.setSourcePropertyName( config.getSourcePropertyName() );
949 			transfer.setSourcePath( config.getSourcePath() );
950 			transfer.setTargetStepName( config.getTargetStepName() );
951 			transfer.setTargetPropertyName( config.getTargetPropertyName() );
952 			transfer.setTargetPath( config.getTargetPath() );
953 			
954 			listModel.addElement( name );
955 			transferList.setSelectedIndex( listModel.getSize()-1 );
956 		}
957 	}
958 	
959 	private final class DeleteAction extends AbstractAction
960 	{
961 		public DeleteAction()
962 		{
963 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ));
964 			putValue( Action.SHORT_DESCRIPTION, "Deletes the selected Property Transfer" );
965 		}
966 		
967 		public void actionPerformed(ActionEvent e)
968 		{
969 			if( UISupport.confirm( "Delete selected transfer", "Delete Transfer" )) 
970 			{
971 				transferList.setSelectedIndex( -1 );
972 				
973 				int ix = transferList.getSelectedIndex();
974 				transferStep.removeTransferAt( ix );
975 				listModel.remove( ix );
976 				
977 				if( listModel.getSize() > 0 )
978 				{
979 					transferList.setSelectedIndex( ix > listModel.getSize()-1 ? listModel.getSize()-1 : ix );
980 				}
981 			}
982 		}
983 	}
984 	
985 	private final class ClearLogAction extends AbstractAction
986 	{
987 		public ClearLogAction()
988 		{
989 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_properties.gif" ));
990 			putValue( Action.SHORT_DESCRIPTION, "Clears the property-transfer log" );
991 		}
992 		
993 		public void actionPerformed(ActionEvent e)
994 		{
995 			transfersTableModel.clear();
996 		}
997 	}
998 	
999 	private final class RenameAction extends AbstractAction
1000 	{
1001 		public RenameAction()
1002 		{
1003 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/rename.gif" ));
1004 			putValue( Action.SHORT_DESCRIPTION, "Renames the selected Property Transfer" );
1005 		}
1006 		
1007 		public void actionPerformed(ActionEvent e)
1008 		{
1009 			PropertyTransfer transfer = getCurrentTransfer();
1010 			
1011 			String newName = UISupport.prompt( "Specify new name for transfer", "Rename Transfer", transfer.getName() );
1012 			
1013 			if( newName != null && !transfer.getName().equals( newName )) 
1014 			{
1015 				listModel.setElementAt( newName, transferList.getSelectedIndex() );
1016 				transfer.setName( newName );
1017 			}
1018 		}
1019 	}
1020 	
1021 	private final class DeclareNamespacesAction extends AbstractAction
1022 	{
1023 		public DeclareNamespacesAction()
1024 		{
1025 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/declareNs.gif" ));
1026 			putValue( Action.SHORT_DESCRIPTION, "Declare available response/request namespaces in source/target expressions" );
1027 		}
1028 		
1029 		public void actionPerformed(ActionEvent e)
1030 		{
1031 			try
1032 			{
1033 				TestPropertyHolder previousStep = getCurrentTransfer().getSourceStep();
1034 				
1035 				if ( previousStep instanceof WsdlTestRequestStep )
1036 				{
1037 					WsdlTestRequest testRequest = ((WsdlTestRequestStep)previousStep).getTestRequest();
1038 					sourceArea.setText( XmlUtils.declareXPathNamespaces( testRequest.getOperation().getInterface() ) + sourceArea.getText() );
1039 				}
1040 				else UISupport.showErrorMessage( "Property Source is not a Request" );
1041 
1042 				TestPropertyHolder nextStep = getCurrentTransfer().getTargetStep();
1043 				
1044 				if (nextStep instanceof WsdlTestRequestStep)
1045 				{
1046 					WsdlTestRequest testRequest = ((WsdlTestRequestStep)nextStep).getTestRequest();
1047 					targetArea.setText( XmlUtils.declareXPathNamespaces( testRequest.getOperation().getInterface()) + targetArea.getText() );
1048 				}
1049 				else UISupport.showErrorMessage( "Property Target is not a Request" );
1050 			}
1051 			catch (Exception e1)
1052 			{
1053 				UISupport.showErrorMessage( e1 );
1054 			}
1055 		}
1056 	}
1057 	
1058 	private final class RunAllAction extends AbstractAction
1059 	{
1060 		public RunAllAction()
1061 		{
1062 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run_all.gif" ));
1063 			putValue( Action.SHORT_DESCRIPTION, "Runs all Property Transfers" );
1064 		}
1065 		
1066 		public void actionPerformed(ActionEvent e)
1067 		{
1068 			if( listModel.getSize() == 0 )
1069 			{
1070 				UISupport.showErrorMessage( "Missing transfers!" );
1071 				return;
1072 			}
1073 			
1074 			WsdlTestRunContext context = new WsdlTestRunContext( transferStep );
1075 			ValueTransferResult result = (ValueTransferResult) transferStep.run( null, context );
1076 			transfersTableModel.addResult( result );
1077 		}
1078 	}
1079 	
1080 	private final class RunAction extends AbstractAction
1081 	{
1082 		public RunAction()
1083 		{
1084 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run.gif" ));
1085 			putValue( Action.SHORT_DESCRIPTION, "Runs selected PropertyTransfer" );
1086 		}
1087 		
1088 		public void actionPerformed(ActionEvent e)
1089 		{
1090 			if( transferList.getSelectedIndex() == -1 )
1091 			{
1092 				UISupport.showErrorMessage( "No transfer selectd!" );
1093 				return;
1094 			}
1095 			
1096 			WsdlTestRunContext context = new WsdlTestRunContext( transferStep );
1097 			ValueTransferResult result = (ValueTransferResult) transferStep.run( null, context, getCurrentTransfer() );
1098 			transfersTableModel.addResult( result );
1099 		}
1100 	}
1101 	
1102 	public boolean onClose( boolean canCancel )
1103 	{
1104 		super.release();
1105 		transferStep.getTestCase().getTestSuite().removeTestSuiteListener( testSuiteListener ); 
1106 		transferStep.getTestCase().removeTestRunListener( testRunListener );
1107 		
1108 		PropertyTransfer transfer = getCurrentTransfer();
1109 		
1110 		if( transfer != null )
1111 		{
1112 			transfer.removePropertyChangeListener( transferPropertyChangeListener );
1113 		}
1114 		
1115 		TestPropertyHolder item = ( TestPropertyHolder ) sourceStepCombo.getSelectedItem();
1116 		if( item != null )
1117 		{
1118 			item.removeTestPropertyListener( sourceStepPropertiesListener );
1119 		}
1120 
1121 		item = ( TestPropertyHolder ) targetStepCombo.getSelectedItem();
1122 		if( item != null )
1123 		{
1124 			item.removeTestPropertyListener( targetStepPropertiesListener );
1125 		}
1126 
1127 		componentEnabler.release();
1128 		if( resultDesktopPanel != null )
1129 			SoapUI.getDesktop().closeDesktopPanel( resultDesktopPanel );
1130 		
1131 		return true;
1132 	}
1133 
1134 	public JComponent getComponent()
1135 	{
1136 		return this;
1137 	}
1138 	
1139 	protected JTextArea getSourceArea()
1140 	{
1141 		return sourceArea;
1142 	}
1143 
1144 	protected JTextArea getTargetArea()
1145 	{
1146 		return targetArea;
1147 	}
1148 
1149 	public boolean dependsOn(ModelItem modelItem)
1150 	{
1151 		return modelItem == transferStep || modelItem == transferStep.getTestCase() ||
1152 				modelItem == transferStep.getTestCase().getTestSuite() ||
1153 				modelItem == transferStep.getTestCase().getTestSuite().getProject();
1154 	}
1155 
1156 	public boolean selectTransfer(PropertyTransfer transfer)
1157 	{
1158 		for( int c = 0; c < transferStep.getTransferCount(); c++ )
1159 		{
1160 			if( transferStep.getTransferAt( c ) == transfer )
1161 			{
1162 				transferList.setSelectedIndex( c );
1163 				return true;
1164 			}
1165 		}
1166 		
1167 		return false;
1168 	}
1169 	
1170 	private class TransfersTableModel extends AbstractTableModel
1171 	{
1172 		private List<ValueTransferResult> results = new ArrayList<ValueTransferResult>();
1173 		
1174 		public synchronized int getRowCount()
1175 		{
1176 			int sum = 0; 
1177 			for( ValueTransferResult result : results )
1178 			{
1179 				sum += result.getTransferCount();
1180 			}
1181 			
1182 			return sum;
1183 		}
1184 
1185 		public synchronized void clear()
1186 		{
1187 			results.clear();
1188 			fireTableDataChanged();
1189 			logInspector.setTitle(  "Transfer Log (0)" );
1190 		}
1191 
1192 		public synchronized void addResult( ValueTransferResult result )
1193 		{
1194 			int rowCount = getRowCount();
1195 			results.add( result );
1196 			fireTableRowsInserted( rowCount, rowCount + result.getTransferCount() );
1197 			
1198 			logInspector.setTitle(  "Transfer Log (" + getRowCount() + ")" );
1199 			inspectorPanel.activate( logInspector );
1200 		}
1201 		
1202 		public int getColumnCount()
1203 		{
1204 			return 3;
1205 		}
1206 		
1207 		public String getColumnName(int column)
1208 		{
1209 			switch( column )
1210 			{
1211 			case 0 : return "Timestamp";
1212 			case 1 : return "Transfer Name";
1213 			case 2 : return "Transferred Values";
1214 			}
1215 			
1216 			return null;
1217 		}
1218 
1219 		public synchronized Object getValueAt(int rowIndex, int columnIndex)
1220 		{
1221 			// find correct transfer
1222 			ValueTransferResult result = null;
1223 			int sum = 0;
1224 			
1225 			for( int c = 0; c < results.size(); c++ )
1226 			{
1227 				if( sum + results.get( c ).getTransferCount() > rowIndex )
1228 				{
1229 					result = results.get( c );
1230 					break;
1231 				}
1232 				else
1233 				{
1234 					sum += results.get( c ).getTransferCount();
1235 				}
1236 			}
1237 			
1238 			if( result != null )
1239 			{
1240 				switch( columnIndex )
1241 				{
1242 					case 0 : return new Date( result.getTimeStamp() ).toString();
1243 					case 1 : return result.getTransferAt( rowIndex-sum ).getName();
1244 					case 2 : return Arrays.toString( result.getTransferredValuesAt( rowIndex-sum ) );
1245 				}
1246 			}
1247 			
1248 			return null;
1249 		}
1250 		
1251 	}
1252 	
1253 	private class InternalTestRunListener extends TestRunListenerAdapter
1254 	{
1255 		@Override
1256 		public void afterStep( TestRunner testRunner, TestRunContext runContext, TestStepResult result )
1257 		{
1258 			if( result.getTestStep() == transferStep )
1259 			{
1260 				transfersTableModel.addResult( ( ValueTransferResult ) result );
1261 			}
1262 		}
1263 	}
1264 	
1265 	private class StepComboRenderer extends DefaultListCellRenderer
1266 	{
1267 		@Override
1268 		public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus )
1269 		{
1270 			Component result = super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
1271 			
1272 			if( value instanceof TestModelItem )
1273 			{
1274 				TestModelItem item = ( TestModelItem ) value;
1275 				setIcon( item.getIcon() );
1276 				setText( item.getName() );
1277 			}
1278 			else if( value == PropertyExpansionUtils.getGlobalProperties())
1279 			{
1280 				setText( "Global" );
1281 			}
1282 			
1283 			return result;
1284 		}
1285 	}
1286 	
1287 	private class PropertyComboRenderer extends DefaultListCellRenderer
1288 	{
1289 		@Override
1290 		public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus )
1291 		{
1292 			Component result = super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
1293 			
1294 			if( value != null )
1295 			{
1296 				TestProperty item = ( TestProperty ) value;
1297 				setText( item.getName() );
1298 			}
1299 			
1300 			return result;
1301 		}
1302 	}
1303 }