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