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